mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
Compare commits
480 Commits
Author | SHA1 | Date | |
---|---|---|---|
729a4fe37e | |||
97e4b53cf3 | |||
c850212288 | |||
600afaf530 | |||
c21dc681c9 | |||
f4ad5d36ec | |||
f627fe3a39 | |||
7b6dc33824 | |||
b4ee994515 | |||
b1dd62078f | |||
bf3efdb89c | |||
354de5f13f | |||
b4ffb8af45 | |||
a49b12b66b | |||
1573e1eb97 | |||
9b9daaee6f | |||
99643ba2e6 | |||
08ea5ebe1f | |||
cb1c7ea12c | |||
1026100c9d | |||
943ba3a2da | |||
bfa9f1e69b | |||
4d150e9340 | |||
2019028688 | |||
b4728f2e1d | |||
d8706af2ea | |||
08e19602f7 | |||
b12b500bfc | |||
e786ea601e | |||
36da8117c0 | |||
6d5afdaa5f | |||
52dd3d2446 | |||
ecc32ddd18 | |||
38c29fc242 | |||
40f4dc9ecf | |||
95a6689077 | |||
c5f875dc5f | |||
89b5e819a3 | |||
6585381230 | |||
f3ce7ff86c | |||
e4a65c72dd | |||
f14a73584f | |||
fffb52dd93 | |||
71f9ed3099 | |||
e293b89f6b | |||
8a284e7c74 | |||
22ed153004 | |||
ff9f09a24e | |||
7eb2a6b709 | |||
f2e9bb54f0 | |||
ac6667b1c9 | |||
7d78a3aeef | |||
aa088721c3 | |||
97f7050d7d | |||
e21be3382b | |||
72cd753c02 | |||
c8d7b6fa92 | |||
8c70513a24 | |||
35062ceb99 | |||
f05afb5468 | |||
ecba117dc0 | |||
d2a1f41750 | |||
be777b8525 | |||
3881af4bbe | |||
933e0f5280 | |||
149c1c2f1b | |||
6cc3212605 | |||
e19aa1d43a | |||
69a366dced | |||
c9ef731fd0 | |||
cd97bdb30f | |||
3bcf390484 | |||
7fa1c11833 | |||
ab0f2c13af | |||
66e5fda418 | |||
e06d603154 | |||
392b0679c9 | |||
336cc9f336 | |||
0bd96f339e | |||
ce0bf6269b | |||
fdaba72974 | |||
a2d98ddde8 | |||
51bfe9eaf6 | |||
a25cf4d188 | |||
ede1146ddc | |||
b916ed3cae | |||
9d5ce45f3b | |||
29cba22405 | |||
b79301a5bd | |||
ef9c3ef1cb | |||
1f620828c2 | |||
f20dbbbd74 | |||
00046d309d | |||
7b7edc9029 | |||
bd208fcec6 | |||
f233d27b78 | |||
42e8667773 | |||
c0361e8546 | |||
3fbbbf8541 | |||
e5684c6127 | |||
0233e0eeec | |||
7fbd3657e8 | |||
005af7f7b7 | |||
94f8f74f51 | |||
f391186749 | |||
e4340a7536 | |||
dd2792b204 | |||
08ee5385ec | |||
73eb517b86 | |||
4b29aef048 | |||
cb7baee045 | |||
85ca5027f4 | |||
50ecc97284 | |||
d382734698 | |||
f91dc7fb6e | |||
0d03c1d4da | |||
68b6136989 | |||
930bb4ba97 | |||
18f129a712 | |||
bb60e68b9d | |||
a70468a2ea | |||
09c89bcd20 | |||
cc365a8175 | |||
ff0d3292a4 | |||
1fe0bcacc0 | |||
f74c22e851 | |||
1ea662a08d | |||
881bb62f88 | |||
f6ef8b41df | |||
c1640ed16c | |||
dd596a5c6c | |||
29f78e0426 | |||
407bf27401 | |||
85df7ce2da | |||
ad40511358 | |||
e8dbdee238 | |||
c784e8170e | |||
31a4aff1f8 | |||
89ca155c43 | |||
908fa2c6c2 | |||
f45d582957 | |||
eb3f4216d4 | |||
e0cdcb6e30 | |||
a7056f7cce | |||
14a6cec6d1 | |||
3b576ae12d | |||
2695985da0 | |||
85e00b2aab | |||
05b12602d4 | |||
374d5ae5a1 | |||
fd11711673 | |||
8282385074 | |||
ef7638d45a | |||
d8dafa7ecc | |||
faacd76f62 | |||
c21e0f6cf3 | |||
9785a89013 | |||
b015836e7b | |||
a9dae931c7 | |||
071cb86b45 | |||
c6743988d3 | |||
bb33427f65 | |||
4889e655eb | |||
aa4fc3dd29 | |||
188611a767 | |||
4872091442 | |||
5600783151 | |||
abe1fa5bd4 | |||
96d965fe04 | |||
9c566564e1 | |||
fc5906dbd4 | |||
fe3aeb36c5 | |||
591a417b7d | |||
d4d35e6b2b | |||
f97de599dd | |||
f01996ae99 | |||
7735c80d0e | |||
2b3d7be9cb | |||
9fa2cc45d2 | |||
7a0c0ca613 | |||
48a8dbece9 | |||
67d54ef3d5 | |||
d5fa20dd33 | |||
be3f47b374 | |||
9ea13e790d | |||
f13f49ccb5 | |||
2cc00ab853 | |||
ed402d7583 | |||
acf990743e | |||
587eb5fdb4 | |||
f151d435a8 | |||
8f961ac397 | |||
cf5db8f663 | |||
54beabb9dc | |||
41752ad5a2 | |||
8349316fcd | |||
4229e9b2ca | |||
5e86014443 | |||
d6381eeaff | |||
45f7f9b07a | |||
6dc1892494 | |||
e9b2d275c8 | |||
7b78a29f3f | |||
f270d317bb | |||
cc3acf8102 | |||
21fdcf41c3 | |||
bcb63b8ccb | |||
22e46ea6cc | |||
aa625f5196 | |||
1f66b06f93 | |||
e4427cb017 | |||
93afe5113a | |||
73681a30e5 | |||
e9b5be9adb | |||
83d679bf72 | |||
b9f83dc77d | |||
7ba14c2097 | |||
3014082ba2 | |||
12016d35bb | |||
6db795401a | |||
43ca8f7050 | |||
063c5a5ace | |||
fcab026512 | |||
95f505a457 | |||
577dc1fa00 | |||
dc625490f8 | |||
c651670222 | |||
459df4e0c9 | |||
eb53fa8d0e | |||
9e3e4368c7 | |||
98b6d7f283 | |||
225a0eccdd | |||
1b22e2b320 | |||
44119db436 | |||
943b6bc51b | |||
4a22057138 | |||
ba78199dd1 | |||
9b51094743 | |||
3c5fd4ba84 | |||
ea722615c4 | |||
6f7d7e645a | |||
8fb54f47ea | |||
447fad34c7 | |||
d263607b27 | |||
b54fb24745 | |||
0cf3b25d50 | |||
033f0b01b7 | |||
f4e15dd93d | |||
3663b9193d | |||
591eb2ea38 | |||
69f5d19455 | |||
4d775008df | |||
4565f7f8b9 | |||
fdfb60c633 | |||
62082bdb01 | |||
8cd6e13308 | |||
31243cdc20 | |||
0aa8c03bea | |||
51a66d5919 | |||
29fa74f621 | |||
4c4691dc2e | |||
8f10c9056c | |||
3bb04e82a5 | |||
9b41b95934 | |||
fb56f89ced | |||
56cbdd1403 | |||
50e8f7ca86 | |||
5ebd3594e4 | |||
d51adfe7bc | |||
a446cd692d | |||
4d6e20a96d | |||
9ebfc54eb5 | |||
f5efb50871 | |||
4cd31cf3c3 | |||
6a2d214b55 | |||
01c682c41e | |||
7d5da1df5e | |||
2ca20f9050 | |||
99f3e37ccf | |||
dcc0201b45 | |||
66aa3574d9 | |||
40d3f1c1fe | |||
1e2ce29f57 | |||
74018167ff | |||
0625bc7688 | |||
fb8cda9d90 | |||
a213aed4da | |||
17967da676 | |||
1f6277e35b | |||
006850ea5e | |||
c3359dec1b | |||
15fe73a252 | |||
246f7bf555 | |||
759602af64 | |||
273c2f3a8f | |||
a1f6e38624 | |||
e3fdd6d94a | |||
27f89bdcc3 | |||
15623ec487 | |||
bc112eaae1 | |||
70e368a1c6 | |||
7d9f6096fe | |||
06ad35c42b | |||
b7e8275d90 | |||
b9cf0a9c9a | |||
14bc842e77 | |||
7d9217b14a | |||
8a13ed59bc | |||
43500a9983 | |||
8e6cbc1021 | |||
d35e92569d | |||
b6067c7569 | |||
8f684f703e | |||
5662f80c43 | |||
5e044e5bba | |||
732ce7a27c | |||
fa6bf597cf | |||
b7a8e8e544 | |||
7429d1f9cc | |||
2c25153506 | |||
401ea05dd8 | |||
b23ba00cff | |||
343a8bef22 | |||
181fde254f | |||
0080feb9af | |||
91a2c4743e | |||
a50c12b6ae | |||
4b2e6b54a7 | |||
adaf843048 | |||
e96a0bf799 | |||
6e7f22ac3a | |||
3b16946c25 | |||
1d92d78de7 | |||
eb624c929d | |||
e72b153f87 | |||
7b4ded306b | |||
49ae944d65 | |||
2585360a3e | |||
2ffc96d0b2 | |||
9a123052a0 | |||
9b9d13ab0d | |||
6ca4e14b29 | |||
6b32aca094 | |||
b251c51936 | |||
a9b17681b0 | |||
60bad8279e | |||
a871dcaebe | |||
cef5b27b48 | |||
c5bbedfabb | |||
e7367c75aa | |||
8d8c048924 | |||
4f646543fc | |||
1885ecc958 | |||
71b7b4e0f4 | |||
4e567d0483 | |||
c4f7cdeec4 | |||
c844d7ac2e | |||
496e782544 | |||
cfd7577e1b | |||
d5df185ac6 | |||
c94ef092ff | |||
74db69dcb7 | |||
fe2dd1e843 | |||
1dc557456e | |||
45deb2472c | |||
8fbaf06cbe | |||
08dce576bd | |||
4b28518cd5 | |||
c9ee528880 | |||
0e869e3aab | |||
6fe764540c | |||
81abd9bb67 | |||
c602d38c8e | |||
c45adddd8e | |||
25648ed9c0 | |||
c8daa48e66 | |||
6a95179456 | |||
ea4dec96e6 | |||
44ed61b2c3 | |||
64f10f8f69 | |||
7b18bfd1a7 | |||
6e946bf872 | |||
e01a081f2f | |||
df0d34dbd4 | |||
b18262f6d0 | |||
d30b775d25 | |||
82c3cccd72 | |||
1814b3b593 | |||
ff7a28274f | |||
8909086b58 | |||
a851dd1198 | |||
9817955fef | |||
774d8ffdba | |||
4a80874da9 | |||
4e19ec93dc | |||
976d3332d4 | |||
2d02ae5e97 | |||
967001d9d3 | |||
09ec40e38d | |||
345c7da384 | |||
9350595158 | |||
a0d5826cbc | |||
7859bf2c60 | |||
acc3ae6e62 | |||
d1c4897f31 | |||
4a7dd400fe | |||
687c50dc13 | |||
b40cdcb5bd | |||
b9338c72c9 | |||
8f6273e9d0 | |||
1fd708cda8 | |||
ac3126b6cf | |||
7c4ea39774 | |||
27fbea2b5a | |||
f34163a065 | |||
6570aefeec | |||
5c5031fd69 | |||
8e0f3c7ddf | |||
8a5c3af949 | |||
d5bd3be8de | |||
bb2c16386b | |||
ec451b5908 | |||
ae3d4b9d28 | |||
af96150f5c | |||
10b152ac3e | |||
9acf5587fa | |||
c302116e73 | |||
ff36154c4b | |||
d12ad1128e | |||
cb2f5c154c | |||
c3cdd516ef | |||
19743f3085 | |||
5db06d99bb | |||
384200f27e | |||
5e712ca255 | |||
d405f28622 | |||
2f975f870a | |||
4471d4aba1 | |||
3945c77668 | |||
6c27af35e9 | |||
2a7e8f7d94 | |||
dd0144c3cd | |||
03130b7565 | |||
4eb462426d | |||
1209022492 | |||
bb88038c17 | |||
37b1b35035 | |||
190b2dd922 | |||
dd7d78cd60 | |||
9abe1e2790 | |||
7bebfebe5f | |||
2d8dc83480 | |||
382cf76503 | |||
9a5f5114c4 | |||
047c2929c1 | |||
9525663014 | |||
3ff1f28533 | |||
4fcf06a164 | |||
9b0660e751 | |||
543589a97b | |||
82d0c87934 | |||
4f8a396692 | |||
6141a26a09 | |||
bb7596045f | |||
16c68ee132 | |||
e0c42ae415 | |||
6ae9f436a9 | |||
58eb8ad11f | |||
c045288ce4 | |||
f9618d30f3 | |||
e3bf6b968c | |||
d85f0e1060 | |||
89cb9673d4 | |||
f0dbd8b78d | |||
626af1ddc1 | |||
2c4369a653 | |||
b8aeda794c | |||
2dc4ae78fc | |||
bd67c9e620 | |||
0f0765e517 |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1,3 +1,4 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: Alexays
|
||||||
custom: https://paypal.me/ARouillard
|
custom: https://paypal.me/ARouillard
|
||||||
|
22
.github/workflows/freebsd.yml
vendored
Normal file
22
.github/workflows/freebsd.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
name: freebsd
|
||||||
|
|
||||||
|
on: [ push, pull_request ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
clang:
|
||||||
|
runs-on: macos-latest # until https://github.com/actions/runner/issues/385
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Test in FreeBSD VM
|
||||||
|
uses: vmactions/freebsd-vm@v0.0.9 # aka FreeBSD 12.2
|
||||||
|
with:
|
||||||
|
usesh: true
|
||||||
|
prepare: |
|
||||||
|
export CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib # sndio
|
||||||
|
sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
|
||||||
|
pkg install -y git # subprojects/date
|
||||||
|
pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu sndio \
|
||||||
|
libfmt libmpdclient libudev-devd meson pkgconf pulseaudio scdoc spdlog
|
||||||
|
run: |
|
||||||
|
meson build -Dman-pages=enabled
|
||||||
|
ninja -C build
|
17
.travis.yml
17
.travis.yml
@ -1,4 +1,4 @@
|
|||||||
sudo: false
|
language: cpp
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
@ -11,6 +11,7 @@ env:
|
|||||||
- distro: archlinux
|
- distro: archlinux
|
||||||
- distro: fedora
|
- distro: fedora
|
||||||
- distro: alpine
|
- distro: alpine
|
||||||
|
- distro: opensuse
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- docker pull alexays/waybar:${distro}
|
- docker pull alexays/waybar:${distro}
|
||||||
@ -21,3 +22,17 @@ script:
|
|||||||
- echo ADD . /root >> Dockerfile
|
- echo ADD . /root >> Dockerfile
|
||||||
- docker build -t waybar .
|
- docker build -t waybar .
|
||||||
- docker run waybar /bin/sh -c "cd /root && meson build -Dman-pages=enabled && ninja -C build"
|
- docker run waybar /bin/sh -c "cd /root && meson build -Dman-pages=enabled && ninja -C build"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
include:
|
||||||
|
- os: freebsd
|
||||||
|
compiler: clang
|
||||||
|
env:
|
||||||
|
before_install:
|
||||||
|
- export CPPFLAGS+=-isystem/usr/local/include LDFLAGS+=-L/usr/local/lib # sndio
|
||||||
|
- sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
|
||||||
|
- sudo pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu sndio
|
||||||
|
libfmt libmpdclient libudev-devd meson pulseaudio scdoc spdlog
|
||||||
|
script:
|
||||||
|
- meson build -Dman-pages=enabled
|
||||||
|
- ninja -C build
|
||||||
|
@ -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 libnl3-dev pulseaudio-dev libmpdclient-dev scdoc
|
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
|
||||||
|
@ -3,4 +3,4 @@
|
|||||||
FROM archlinux/base:latest
|
FROM archlinux/base:latest
|
||||||
|
|
||||||
RUN pacman -Syu --noconfirm && \
|
RUN pacman -Syu --noconfirm && \
|
||||||
pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp scdoc --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
|
||||||
|
@ -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 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 && \
|
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 && \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# vim: ft=Dockerfile
|
# vim: ft=Dockerfile
|
||||||
|
|
||||||
FROM fedora:30
|
FROM fedora:32
|
||||||
|
|
||||||
RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel scdoc -y && \
|
RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel pugixml-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel scdoc -y && \
|
||||||
dnf group install "C Development Tools and Libraries" -y && \
|
dnf group install "C Development Tools and Libraries" -y && \
|
||||||
dnf clean all -y
|
dnf clean all -y
|
||||||
|
@ -4,4 +4,4 @@ FROM opensuse/tumbleweed:latest
|
|||||||
|
|
||||||
RUN zypper -n up && \
|
RUN zypper -n up && \
|
||||||
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 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 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 scdoc
|
||||||
|
2
Makefile
2
Makefile
@ -1,6 +1,6 @@
|
|||||||
.PHONY: build build-debug run clean default install
|
.PHONY: build build-debug run clean default install
|
||||||
|
|
||||||
default: run
|
default: build
|
||||||
|
|
||||||
build:
|
build:
|
||||||
meson build
|
meson build
|
||||||
|
55
README.md
55
README.md
@ -5,12 +5,13 @@
|
|||||||
[AUR](https://aur.archlinux.org/packages/waybar-git/), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar)<br>
|
[AUR](https://aur.archlinux.org/packages/waybar-git/), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar)<br>
|
||||||
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
|
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
|
||||||
|
|
||||||
**Current features**
|
#### Current features
|
||||||
- Sway (Workspaces, Binding mode, Focused window name)
|
- Sway (Workspaces, Binding mode, 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
|
||||||
- Network
|
- Network
|
||||||
|
- Bluetooth
|
||||||
- Pulseaudio
|
- Pulseaudio
|
||||||
- Disk
|
- Disk
|
||||||
- Memory
|
- Memory
|
||||||
@ -21,11 +22,21 @@
|
|||||||
- Multiple output configuration
|
- Multiple output configuration
|
||||||
- And much more customizations
|
- And much more customizations
|
||||||
|
|
||||||
**Configuration and Styling**
|
#### Configuration and Styling
|
||||||
|
|
||||||
[See the wiki for more details](https://github.com/Alexays/Waybar/wiki).
|
[See the wiki for more details](https://github.com/Alexays/Waybar/wiki).
|
||||||
|
|
||||||
**How to build**
|
### Installation
|
||||||
|
|
||||||
|
Waybar is available from a number of Linux distributions:
|
||||||
|
|
||||||
|
[](https://repology.org/project/waybar/versions)
|
||||||
|
|
||||||
|
An Ubuntu PPA with more recent versions is available
|
||||||
|
[here](https://launchpad.net/~nschloe/+archive/ubuntu/waybar).
|
||||||
|
|
||||||
|
|
||||||
|
#### Building from source
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/Alexays/Waybar
|
$ git clone https://github.com/Alexays/Waybar
|
||||||
@ -43,26 +54,52 @@ $ waybar
|
|||||||
```
|
```
|
||||||
gtkmm3
|
gtkmm3
|
||||||
jsoncpp
|
jsoncpp
|
||||||
libinput
|
|
||||||
|
|
||||||
libsigc++
|
libsigc++
|
||||||
fmt
|
fmt
|
||||||
wayland
|
wayland
|
||||||
wlroots
|
chrono-date
|
||||||
|
spdlog
|
||||||
libgtk-3-dev [gtk-layer-shell]
|
libgtk-3-dev [gtk-layer-shell]
|
||||||
gobject-introspection [gtk-layer-shell]
|
gobject-introspection [gtk-layer-shell]
|
||||||
libgirepository1.0-dev [gtk-layer-shell]
|
libgirepository1.0-dev [gtk-layer-shell]
|
||||||
libpulse [Pulseaudio module]
|
libpulse [Pulseaudio module]
|
||||||
libnl [Network module]
|
libnl [Network module]
|
||||||
sway [Sway modules]
|
libappindicator-gtk3 [Tray module]
|
||||||
libdbusmenu-gtk3 [Tray module]
|
libdbusmenu-gtk3 [Tray module]
|
||||||
libmpdclient [MPD module]
|
libmpdclient [MPD module]
|
||||||
|
libsndio [sndio module]
|
||||||
```
|
```
|
||||||
|
|
||||||
On Ubuntu 19.10 you can install all the relevant dependencies using this command:
|
**Build dependencies**
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo apt install libgtkmm-3.0-dev libjsoncpp-dev libinput-dev libsigc++-2.0-dev libpulse-dev libnl-3-dev libdbusmenu-gtk3-dev libnl-genl-3-dev libfmt-dev clang-tidy libmpdclient-dev libwayland-dev libgtk-3-dev gobject-introspection libgirepository1.0-dev scdoc
|
cmake
|
||||||
|
meson
|
||||||
|
scdoc
|
||||||
|
wayland-protocols
|
||||||
|
```
|
||||||
|
|
||||||
|
On Ubuntu you can install all the relevant dependencies using this command (tested with 19.10 and 20.04):
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install \
|
||||||
|
clang-tidy \
|
||||||
|
gobject-introspection \
|
||||||
|
libdbusmenu-gtk3-dev \
|
||||||
|
libfmt-dev \
|
||||||
|
libgirepository1.0-dev \
|
||||||
|
libgtk-3-dev \
|
||||||
|
libgtkmm-3.0-dev \
|
||||||
|
libinput-dev \
|
||||||
|
libjsoncpp-dev \
|
||||||
|
libmpdclient-dev \
|
||||||
|
libnl-3-dev \
|
||||||
|
libnl-genl-3-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libsigc++-2.0-dev \
|
||||||
|
libspdlog-dev \
|
||||||
|
libwayland-dev \
|
||||||
|
scdoc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,15 +10,15 @@ namespace waybar {
|
|||||||
class ALabel : public AModule {
|
class ALabel : public AModule {
|
||||||
public:
|
public:
|
||||||
ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format,
|
ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format,
|
||||||
uint16_t interval = 0, bool ellipsize = false);
|
uint16_t interval = 0, bool ellipsize = false, bool enable_click = false, bool enable_scroll = false);
|
||||||
virtual ~ALabel() = default;
|
virtual ~ALabel() = default;
|
||||||
virtual auto update() -> void;
|
virtual auto update() -> void;
|
||||||
virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0);
|
virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0);
|
||||||
|
virtual std::string getIcon(uint16_t, std::vector<std::string> &alts, uint16_t max = 0);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Gtk::Label label_;
|
Gtk::Label label_;
|
||||||
std::string format_;
|
std::string format_;
|
||||||
std::string click_param;
|
|
||||||
const std::chrono::seconds interval_;
|
const std::chrono::seconds interval_;
|
||||||
bool alt_ = false;
|
bool alt_ = false;
|
||||||
std::string default_format_;
|
std::string default_format_;
|
||||||
|
@ -4,14 +4,15 @@
|
|||||||
#include <glibmm/markup.h>
|
#include <glibmm/markup.h>
|
||||||
#include <gtkmm/eventbox.h>
|
#include <gtkmm/eventbox.h>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
#include "IModule.hpp"
|
#include "IModule.hpp"
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
class AModule : public IModule {
|
class AModule : public IModule {
|
||||||
public:
|
public:
|
||||||
AModule(const Json::Value &, const std::string &, const std::string &,
|
AModule(const Json::Value &, const std::string &, const std::string &, bool enable_click = false,
|
||||||
bool enable_click = false, bool enable_scroll = false);
|
bool enable_scroll = false);
|
||||||
virtual ~AModule();
|
virtual ~AModule();
|
||||||
virtual auto update() -> void;
|
virtual auto update() -> void;
|
||||||
virtual operator Gtk::Widget &();
|
virtual operator Gtk::Widget &();
|
||||||
@ -24,6 +25,7 @@ class AModule : public IModule {
|
|||||||
SCROLL_DIR getScrollDir(GdkEventScroll *e);
|
SCROLL_DIR getScrollDir(GdkEventScroll *e);
|
||||||
bool tooltipEnabled();
|
bool tooltipEnabled();
|
||||||
|
|
||||||
|
const std::string name_;
|
||||||
const Json::Value &config_;
|
const Json::Value &config_;
|
||||||
Gtk::EventBox event_box_;
|
Gtk::EventBox event_box_;
|
||||||
|
|
||||||
|
@ -7,9 +7,8 @@
|
|||||||
#include <gtkmm/main.h>
|
#include <gtkmm/main.h>
|
||||||
#include <gtkmm/window.h>
|
#include <gtkmm/window.h>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
#include "AModule.hpp"
|
#include "AModule.hpp"
|
||||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
|
||||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
|
||||||
#include "xdg-output-unstable-v1-client-protocol.h"
|
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
@ -18,65 +17,66 @@ class Factory;
|
|||||||
struct waybar_output {
|
struct waybar_output {
|
||||||
Glib::RefPtr<Gdk::Monitor> monitor;
|
Glib::RefPtr<Gdk::Monitor> monitor;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::string identifier;
|
||||||
|
|
||||||
std::unique_ptr<struct zxdg_output_v1, decltype(&zxdg_output_v1_destroy)> xdg_output = {
|
std::unique_ptr<struct zxdg_output_v1, decltype(&zxdg_output_v1_destroy)> xdg_output = {
|
||||||
nullptr, &zxdg_output_v1_destroy};
|
nullptr, &zxdg_output_v1_destroy};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class bar_layer : uint8_t {
|
||||||
|
BOTTOM,
|
||||||
|
TOP,
|
||||||
|
OVERLAY,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bar_margins {
|
||||||
|
int top = 0;
|
||||||
|
int right = 0;
|
||||||
|
int bottom = 0;
|
||||||
|
int left = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BarSurface {
|
||||||
|
protected:
|
||||||
|
BarSurface() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual void setExclusiveZone(bool enable) = 0;
|
||||||
|
virtual void setLayer(bar_layer layer) = 0;
|
||||||
|
virtual void setMargins(const struct bar_margins &margins) = 0;
|
||||||
|
virtual void setPosition(const std::string_view &position) = 0;
|
||||||
|
virtual void setSize(uint32_t width, uint32_t height) = 0;
|
||||||
|
virtual void commit(){};
|
||||||
|
|
||||||
|
virtual ~BarSurface() = default;
|
||||||
|
};
|
||||||
|
|
||||||
class Bar {
|
class Bar {
|
||||||
public:
|
public:
|
||||||
Bar(struct waybar_output *w_output, const Json::Value &);
|
Bar(struct waybar_output *w_output, const Json::Value &);
|
||||||
Bar(const Bar &) = delete;
|
Bar(const Bar &) = delete;
|
||||||
~Bar() = default;
|
~Bar() = default;
|
||||||
|
|
||||||
auto toggle() -> void;
|
void setVisible(bool visible);
|
||||||
|
void toggle();
|
||||||
void handleSignal(int);
|
void handleSignal(int);
|
||||||
|
|
||||||
struct waybar_output *output;
|
struct waybar_output *output;
|
||||||
Json::Value config;
|
Json::Value config;
|
||||||
Gtk::Window window;
|
|
||||||
struct wl_surface * surface;
|
struct wl_surface * surface;
|
||||||
bool visible = true;
|
bool visible = true;
|
||||||
bool vertical = false;
|
bool vertical = false;
|
||||||
|
Gtk::Window window;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr const char *MIN_HEIGHT_MSG =
|
void onMap(GdkEventAny *);
|
||||||
"Requested height: {} exceeds the minimum height: {} required by the modules";
|
|
||||||
static constexpr const char *MIN_WIDTH_MSG =
|
|
||||||
"Requested width: {} exceeds the minimum width: {} required by the modules";
|
|
||||||
static constexpr const char *BAR_SIZE_MSG =
|
|
||||||
"Bar configured (width: {}, height: {}) for output: {}";
|
|
||||||
static constexpr const char *SIZE_DEFINED =
|
|
||||||
"{} size is defined in the config file so it will stay like that";
|
|
||||||
static void layerSurfaceHandleConfigure(void *, struct zwlr_layer_surface_v1 *, uint32_t,
|
|
||||||
uint32_t, uint32_t);
|
|
||||||
static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *);
|
|
||||||
|
|
||||||
#ifdef HAVE_GTK_LAYER_SHELL
|
|
||||||
void initGtkLayerShell();
|
|
||||||
#endif
|
|
||||||
void onConfigure(GdkEventConfigure *ev);
|
|
||||||
void onRealize();
|
|
||||||
void onMap(GdkEventAny *ev);
|
|
||||||
void setExclusiveZone(uint32_t width, uint32_t height);
|
|
||||||
void setSurfaceSize(uint32_t width, uint32_t height);
|
|
||||||
auto setupWidgets() -> void;
|
auto setupWidgets() -> void;
|
||||||
void getModules(const Factory &, const std::string &);
|
void getModules(const Factory &, const std::string &);
|
||||||
void setupAltFormatKeyForModule(const std::string &module_name);
|
void setupAltFormatKeyForModule(const std::string &module_name);
|
||||||
void setupAltFormatKeyForModuleList(const char *module_list_name);
|
void setupAltFormatKeyForModuleList(const char *module_list_name);
|
||||||
|
|
||||||
struct margins {
|
std::unique_ptr<BarSurface> surface_impl_;
|
||||||
int top = 0;
|
bar_layer layer_;
|
||||||
int right = 0;
|
|
||||||
int bottom = 0;
|
|
||||||
int left = 0;
|
|
||||||
} margins_;
|
|
||||||
struct zwlr_layer_surface_v1 *layer_surface_;
|
|
||||||
// use gtk-layer-shell instead of handling layer surfaces directly
|
|
||||||
bool use_gls_ = false;
|
|
||||||
uint32_t width_ = 0;
|
|
||||||
uint32_t height_ = 1;
|
|
||||||
uint8_t anchor_;
|
|
||||||
Gtk::Box left_;
|
Gtk::Box left_;
|
||||||
Gtk::Box center_;
|
Gtk::Box center_;
|
||||||
Gtk::Box right_;
|
Gtk::Box right_;
|
||||||
|
@ -6,14 +6,20 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
|
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
|
|
||||||
|
struct zwlr_layer_shell_v1;
|
||||||
|
struct zwp_idle_inhibitor_v1;
|
||||||
|
struct zwp_idle_inhibit_manager_v1;
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
class Client {
|
class Client {
|
||||||
public:
|
public:
|
||||||
static Client *inst();
|
static Client *inst();
|
||||||
int main(int argc, char *argv[]);
|
int main(int argc, char *argv[]);
|
||||||
|
void reset();
|
||||||
|
|
||||||
Glib::RefPtr<Gtk::Application> gtk_app;
|
Glib::RefPtr<Gtk::Application> gtk_app;
|
||||||
Glib::RefPtr<Gdk::Display> gdk_display;
|
Glib::RefPtr<Gdk::Display> gdk_display;
|
||||||
@ -34,15 +40,18 @@ class Client {
|
|||||||
bool isValidOutput(const Json::Value &config, struct waybar_output &output);
|
bool isValidOutput(const Json::Value &config, struct waybar_output &output);
|
||||||
auto setupConfig(const std::string &config_file) -> void;
|
auto setupConfig(const std::string &config_file) -> void;
|
||||||
auto setupCss(const std::string &css_file) -> void;
|
auto setupCss(const std::string &css_file) -> void;
|
||||||
struct waybar_output &getOutput(void *);
|
struct waybar_output & getOutput(void *);
|
||||||
std::vector<Json::Value> getOutputConfigs(struct waybar_output &output);
|
std::vector<Json::Value> getOutputConfigs(struct waybar_output &output);
|
||||||
|
|
||||||
static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name,
|
static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name,
|
||||||
const char *interface, uint32_t version);
|
const char *interface, uint32_t version);
|
||||||
static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name);
|
static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name);
|
||||||
|
static void handleOutputDone(void *, struct zxdg_output_v1 *);
|
||||||
static void handleOutputName(void *, struct zxdg_output_v1 *, const char *);
|
static void handleOutputName(void *, struct zxdg_output_v1 *, const char *);
|
||||||
|
static void handleOutputDescription(void *, struct zxdg_output_v1 *, const char *);
|
||||||
void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
|
void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
|
||||||
void handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor);
|
void handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor);
|
||||||
|
void handleDeferredMonitorRemoval(Glib::RefPtr<Gdk::Monitor> monitor);
|
||||||
|
|
||||||
Json::Value config_;
|
Json::Value config_;
|
||||||
Glib::RefPtr<Gtk::StyleContext> style_context_;
|
Glib::RefPtr<Gtk::StyleContext> style_context_;
|
||||||
|
@ -1,20 +1,35 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
#ifdef HAVE_LIBDATE
|
||||||
#include "modules/clock.hpp"
|
#include "modules/clock.hpp"
|
||||||
|
#else
|
||||||
|
#include "modules/simpleclock.hpp"
|
||||||
|
#endif
|
||||||
#ifdef HAVE_SWAY
|
#ifdef HAVE_SWAY
|
||||||
#include "modules/sway/mode.hpp"
|
#include "modules/sway/mode.hpp"
|
||||||
#include "modules/sway/window.hpp"
|
#include "modules/sway/window.hpp"
|
||||||
#include "modules/sway/workspaces.hpp"
|
#include "modules/sway/workspaces.hpp"
|
||||||
|
#include "modules/sway/language.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifndef NO_FILESYSTEM
|
#ifdef HAVE_WLR
|
||||||
|
#include "modules/wlr/taskbar.hpp"
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RIVER
|
||||||
|
#include "modules/river/tags.hpp"
|
||||||
|
#endif
|
||||||
|
#if defined(__linux__) && !defined(NO_FILESYSTEM)
|
||||||
#include "modules/battery.hpp"
|
#include "modules/battery.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD)
|
||||||
#include "modules/cpu.hpp"
|
#include "modules/cpu.hpp"
|
||||||
|
#endif
|
||||||
#include "modules/idle_inhibitor.hpp"
|
#include "modules/idle_inhibitor.hpp"
|
||||||
|
#if defined(HAVE_MEMORY_LINUX) || defined(HAVE_MEMORY_BSD)
|
||||||
#include "modules/memory.hpp"
|
#include "modules/memory.hpp"
|
||||||
|
#endif
|
||||||
#include "modules/disk.hpp"
|
#include "modules/disk.hpp"
|
||||||
#if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM)
|
#ifdef HAVE_DBUSMENU
|
||||||
#include "modules/sni/tray.hpp"
|
#include "modules/sni/tray.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_LIBNL
|
#ifdef HAVE_LIBNL
|
||||||
@ -27,11 +42,19 @@
|
|||||||
#include "modules/pulseaudio.hpp"
|
#include "modules/pulseaudio.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_LIBMPDCLIENT
|
#ifdef HAVE_LIBMPDCLIENT
|
||||||
#include "modules/mpd.hpp"
|
#include "modules/mpd/mpd.hpp"
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LIBSNDIO
|
||||||
|
#include "modules/sndio.hpp"
|
||||||
#endif
|
#endif
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "modules/custom.hpp"
|
#include "modules/custom.hpp"
|
||||||
#include "modules/temperature.hpp"
|
#include "modules/temperature.hpp"
|
||||||
|
#if defined(__linux__)
|
||||||
|
# ifdef WANT_RFKILL
|
||||||
|
# include "modules/bluetooth.hpp"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
|
@ -31,19 +31,22 @@ class Battery : public ALabel {
|
|||||||
private:
|
private:
|
||||||
static inline const fs::path data_dir_ = "/sys/class/power_supply/";
|
static inline const fs::path data_dir_ = "/sys/class/power_supply/";
|
||||||
|
|
||||||
void getBatteries();
|
void refreshBatteries();
|
||||||
void worker();
|
void worker();
|
||||||
const std::string getAdapterStatus(uint8_t capacity) const;
|
const std::string getAdapterStatus(uint8_t capacity) const;
|
||||||
const std::tuple<uint8_t, float, std::string> getInfos() const;
|
const std::tuple<uint8_t, float, std::string, float> getInfos();
|
||||||
const std::string formatTimeRemaining(float hoursRemaining);
|
const std::string formatTimeRemaining(float hoursRemaining);
|
||||||
|
|
||||||
std::vector<fs::path> batteries_;
|
int global_watch;
|
||||||
|
std::map<fs::path,int> batteries_;
|
||||||
fs::path adapter_;
|
fs::path adapter_;
|
||||||
int fd_;
|
int battery_watch_fd_;
|
||||||
std::vector<int> wds_;
|
int global_watch_fd_;
|
||||||
|
std::mutex battery_list_mutex_;
|
||||||
std::string old_status_;
|
std::string old_status_;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
|
util::SleeperThread thread_battery_update_;
|
||||||
util::SleeperThread thread_timer_;
|
util::SleeperThread thread_timer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
18
include/modules/bluetooth.hpp
Normal file
18
include/modules/bluetooth.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "util/rfkill.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class Bluetooth : public ALabel {
|
||||||
|
public:
|
||||||
|
Bluetooth(const std::string&, const Json::Value&);
|
||||||
|
~Bluetooth() = default;
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
util::Rfkill rfkill_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
@ -28,9 +28,12 @@ class Clock : public ALabel {
|
|||||||
std::locale locale_;
|
std::locale locale_;
|
||||||
const date::time_zone* time_zone_;
|
const date::time_zone* time_zone_;
|
||||||
bool fixed_time_zone_;
|
bool fixed_time_zone_;
|
||||||
date::year_month_day cached_calendar_ymd_;
|
int time_zone_idx_;
|
||||||
|
date::year_month_day cached_calendar_ymd_ = date::January/1/0;
|
||||||
std::string cached_calendar_text_;
|
std::string cached_calendar_text_;
|
||||||
|
|
||||||
|
bool handleScroll(GdkEventScroll* e);
|
||||||
|
|
||||||
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;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <sys/sysinfo.h>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
@ -20,10 +19,11 @@ class Cpu : public ALabel {
|
|||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static inline const std::string data_dir_ = "/proc/stat";
|
double getCpuLoad();
|
||||||
uint16_t getCpuLoad();
|
|
||||||
std::tuple<uint16_t, std::string> getCpuUsage();
|
std::tuple<uint16_t, std::string> getCpuUsage();
|
||||||
|
std::tuple<float, float, float> getCpuFrequency();
|
||||||
std::vector<std::tuple<size_t, size_t>> parseCpuinfo();
|
std::vector<std::tuple<size_t, size_t>> parseCpuinfo();
|
||||||
|
std::vector<float> parseCpuFrequencies();
|
||||||
|
|
||||||
std::vector<std::tuple<size_t, size_t>> prev_times_;
|
std::vector<std::tuple<size_t, size_t>> prev_times_;
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ class Custom : public ALabel {
|
|||||||
void continuousWorker();
|
void continuousWorker();
|
||||||
void parseOutputRaw();
|
void parseOutputRaw();
|
||||||
void parseOutputJson();
|
void parseOutputJson();
|
||||||
|
void handleEvent();
|
||||||
bool handleScroll(GdkEventScroll* e);
|
bool handleScroll(GdkEventScroll* e);
|
||||||
bool handleToggle(GdkEventButton* const& e);
|
bool handleToggle(GdkEventButton* const& e);
|
||||||
|
|
||||||
|
@ -12,12 +12,13 @@ class IdleInhibitor : public ALabel {
|
|||||||
IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
|
IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
~IdleInhibitor();
|
~IdleInhibitor();
|
||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
static std::list<waybar::AModule*> modules;
|
||||||
|
static bool status;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handleToggle(GdkEventButton* const& e);
|
bool handleToggle(GdkEventButton* const& e);
|
||||||
|
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
std::string status_;
|
|
||||||
struct zwp_idle_inhibitor_v1* idle_inhibitor_;
|
struct zwp_idle_inhibitor_v1* idle_inhibitor_;
|
||||||
int pid_;
|
int pid_;
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <unordered_map>
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
@ -14,11 +15,9 @@ class Memory : public ALabel {
|
|||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static inline const std::string data_dir_ = "/proc/meminfo";
|
|
||||||
void parseMeminfo();
|
void parseMeminfo();
|
||||||
|
|
||||||
unsigned long memtotal_;
|
std::unordered_map<std::string, unsigned long> meminfo_;
|
||||||
unsigned long memfree_;
|
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
};
|
};
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include <mpd/client.h>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <thread>
|
|
||||||
#include "ALabel.hpp"
|
|
||||||
|
|
||||||
namespace waybar::modules {
|
|
||||||
|
|
||||||
class MPD : public ALabel {
|
|
||||||
public:
|
|
||||||
MPD(const std::string&, const Json::Value&);
|
|
||||||
auto update() -> void;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::thread periodic_updater();
|
|
||||||
std::string getTag(mpd_tag_type type, unsigned idx = 0);
|
|
||||||
void setLabel();
|
|
||||||
std::string getStateIcon();
|
|
||||||
std::string getOptionIcon(std::string optionName, bool activated);
|
|
||||||
|
|
||||||
std::thread event_listener();
|
|
||||||
|
|
||||||
// Assumes `connection_lock_` is locked
|
|
||||||
void tryConnect();
|
|
||||||
// If checking errors on the main connection, make sure to lock it using
|
|
||||||
// `connection_lock_` before calling checkErrors
|
|
||||||
void checkErrors(mpd_connection* conn);
|
|
||||||
|
|
||||||
// Assumes `connection_lock_` is locked
|
|
||||||
void fetchState();
|
|
||||||
void waitForEvent();
|
|
||||||
|
|
||||||
bool handlePlayPause(GdkEventButton* const&);
|
|
||||||
|
|
||||||
bool stopped();
|
|
||||||
bool playing();
|
|
||||||
bool paused();
|
|
||||||
|
|
||||||
const std::string module_name_;
|
|
||||||
|
|
||||||
using unique_connection = std::unique_ptr<mpd_connection, decltype(&mpd_connection_free)>;
|
|
||||||
using unique_status = std::unique_ptr<mpd_status, decltype(&mpd_status_free)>;
|
|
||||||
using unique_song = std::unique_ptr<mpd_song, decltype(&mpd_song_free)>;
|
|
||||||
|
|
||||||
// Not using unique_ptr since we don't manage the pointer
|
|
||||||
// (It's either nullptr, or from the config)
|
|
||||||
const char* server_;
|
|
||||||
const unsigned port_;
|
|
||||||
|
|
||||||
unsigned timeout_;
|
|
||||||
|
|
||||||
// We need a mutex here because we can trigger updates from multiple thread:
|
|
||||||
// the event based updates, the periodic updates needed for the elapsed time,
|
|
||||||
// and the click play/pause feature
|
|
||||||
std::mutex connection_lock_;
|
|
||||||
unique_connection connection_;
|
|
||||||
// The alternate connection will be used to wait for events: since it will
|
|
||||||
// be blocking (idle) we can't send commands via this connection
|
|
||||||
//
|
|
||||||
// No lock since only used in the event listener thread
|
|
||||||
unique_connection alternate_connection_;
|
|
||||||
|
|
||||||
// Protect them using the `connection_lock_`
|
|
||||||
unique_status status_;
|
|
||||||
mpd_state state_;
|
|
||||||
unique_song song_;
|
|
||||||
|
|
||||||
// To make sure the previous periodic_updater stops before creating a new one
|
|
||||||
std::mutex periodic_lock_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace waybar::modules
|
|
67
include/modules/mpd/mpd.hpp
Normal file
67
include/modules/mpd/mpd.hpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <mpd/client.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "modules/mpd/state.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class MPD : public ALabel {
|
||||||
|
friend class detail::Context;
|
||||||
|
|
||||||
|
// State machine
|
||||||
|
detail::Context context_{this};
|
||||||
|
|
||||||
|
const std::string module_name_;
|
||||||
|
|
||||||
|
// Not using unique_ptr since we don't manage the pointer
|
||||||
|
// (It's either nullptr, or from the config)
|
||||||
|
const char* server_;
|
||||||
|
const unsigned port_;
|
||||||
|
const std::string password_;
|
||||||
|
|
||||||
|
unsigned timeout_;
|
||||||
|
|
||||||
|
detail::unique_connection connection_;
|
||||||
|
|
||||||
|
detail::unique_status status_;
|
||||||
|
mpd_state state_;
|
||||||
|
detail::unique_song song_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MPD(const std::string&, const Json::Value&);
|
||||||
|
virtual ~MPD() noexcept = default;
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string getTag(mpd_tag_type type, unsigned idx = 0) const;
|
||||||
|
void setLabel();
|
||||||
|
std::string getStateIcon() const;
|
||||||
|
std::string getOptionIcon(std::string optionName, bool activated) const;
|
||||||
|
|
||||||
|
// GUI-side methods
|
||||||
|
bool handlePlayPause(GdkEventButton* const&);
|
||||||
|
void emit() { dp.emit(); }
|
||||||
|
|
||||||
|
// MPD-side, Non-GUI methods.
|
||||||
|
void tryConnect();
|
||||||
|
void checkErrors(mpd_connection* conn);
|
||||||
|
void fetchState();
|
||||||
|
void queryMPD();
|
||||||
|
|
||||||
|
inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; }
|
||||||
|
inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; }
|
||||||
|
inline bool paused() const { return connection_ && state_ == MPD_STATE_PAUSE; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(MPD_NOINLINE)
|
||||||
|
#include "modules/mpd/state.inl.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
217
include/modules/mpd/state.hpp
Normal file
217
include/modules/mpd/state.hpp
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mpd/client.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
class MPD;
|
||||||
|
} // namespace waybar::modules
|
||||||
|
|
||||||
|
namespace waybar::modules::detail {
|
||||||
|
|
||||||
|
using unique_connection = std::unique_ptr<mpd_connection, decltype(&mpd_connection_free)>;
|
||||||
|
using unique_status = std::unique_ptr<mpd_status, decltype(&mpd_status_free)>;
|
||||||
|
using unique_song = std::unique_ptr<mpd_song, decltype(&mpd_song_free)>;
|
||||||
|
|
||||||
|
class Context;
|
||||||
|
|
||||||
|
/// This state machine loosely follows a non-hierarchical, statechart
|
||||||
|
/// pattern, and includes ENTRY and EXIT actions.
|
||||||
|
///
|
||||||
|
/// The State class is the base class for all other states. The
|
||||||
|
/// entry and exit methods are automatically called when entering
|
||||||
|
/// into a new state and exiting from the current state. This
|
||||||
|
/// includes initially entering (Disconnected class) and exiting
|
||||||
|
/// Waybar.
|
||||||
|
///
|
||||||
|
/// The following nested "top-level" states are represented:
|
||||||
|
/// 1. Idle - await notification of MPD activity.
|
||||||
|
/// 2. All Non-Idle states:
|
||||||
|
/// 1. Playing - An active song is producing audio output.
|
||||||
|
/// 2. Paused - The current song is paused.
|
||||||
|
/// 3. Stopped - No song is actively playing.
|
||||||
|
/// 3. Disconnected - periodically attempt MPD (re-)connection.
|
||||||
|
///
|
||||||
|
/// NOTE: Since this statechart is non-hierarchical, the above
|
||||||
|
/// states are flattened into a set.
|
||||||
|
|
||||||
|
class State {
|
||||||
|
public:
|
||||||
|
virtual ~State() noexcept = default;
|
||||||
|
|
||||||
|
virtual void entry() noexcept { spdlog::debug("mpd: ignore entry action"); }
|
||||||
|
virtual void exit() noexcept { spdlog::debug("mpd: ignore exit action"); }
|
||||||
|
|
||||||
|
virtual void play() { spdlog::debug("mpd: ignore play state transition"); }
|
||||||
|
virtual void stop() { spdlog::debug("mpd: ignore stop state transition"); }
|
||||||
|
virtual void pause() { spdlog::debug("mpd: ignore pause state transition"); }
|
||||||
|
|
||||||
|
/// Request state update the GUI.
|
||||||
|
virtual void update() noexcept { spdlog::debug("mpd: ignoring update method request"); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Idle : public State {
|
||||||
|
Context* const ctx_;
|
||||||
|
sigc::connection idle_connection_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Idle(Context* const ctx) : ctx_{ctx} {}
|
||||||
|
virtual ~Idle() noexcept { this->exit(); };
|
||||||
|
|
||||||
|
void entry() noexcept override;
|
||||||
|
void exit() noexcept override;
|
||||||
|
|
||||||
|
void play() override;
|
||||||
|
void stop() override;
|
||||||
|
void pause() override;
|
||||||
|
void update() noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Idle(const Idle&) = delete;
|
||||||
|
Idle& operator=(const Idle&) = delete;
|
||||||
|
|
||||||
|
bool on_io(Glib::IOCondition const&);
|
||||||
|
};
|
||||||
|
|
||||||
|
class Playing : public State {
|
||||||
|
Context* const ctx_;
|
||||||
|
sigc::connection timer_connection_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Playing(Context* const ctx) : ctx_{ctx} {}
|
||||||
|
virtual ~Playing() noexcept { this->exit(); }
|
||||||
|
|
||||||
|
void entry() noexcept override;
|
||||||
|
void exit() noexcept override;
|
||||||
|
|
||||||
|
void pause() override;
|
||||||
|
void stop() override;
|
||||||
|
void update() noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Playing(Playing const&) = delete;
|
||||||
|
Playing& operator=(Playing const&) = delete;
|
||||||
|
|
||||||
|
bool on_timer();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Paused : public State {
|
||||||
|
Context* const ctx_;
|
||||||
|
sigc::connection timer_connection_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Paused(Context* const ctx) : ctx_{ctx} {}
|
||||||
|
virtual ~Paused() noexcept { this->exit(); }
|
||||||
|
|
||||||
|
void entry() noexcept override;
|
||||||
|
void exit() noexcept override;
|
||||||
|
|
||||||
|
void play() override;
|
||||||
|
void stop() override;
|
||||||
|
void update() noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Paused(Paused const&) = delete;
|
||||||
|
Paused& operator=(Paused const&) = delete;
|
||||||
|
|
||||||
|
bool on_timer();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Stopped : public State {
|
||||||
|
Context* const ctx_;
|
||||||
|
sigc::connection timer_connection_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Stopped(Context* const ctx) : ctx_{ctx} {}
|
||||||
|
virtual ~Stopped() noexcept { this->exit(); }
|
||||||
|
|
||||||
|
void entry() noexcept override;
|
||||||
|
void exit() noexcept override;
|
||||||
|
|
||||||
|
void play() override;
|
||||||
|
void pause() override;
|
||||||
|
void update() noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Stopped(Stopped const&) = delete;
|
||||||
|
Stopped& operator=(Stopped const&) = delete;
|
||||||
|
|
||||||
|
bool on_timer();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Disconnected : public State {
|
||||||
|
Context* const ctx_;
|
||||||
|
sigc::connection timer_connection_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Disconnected(Context* const ctx) : ctx_{ctx} {}
|
||||||
|
virtual ~Disconnected() noexcept { this->exit(); }
|
||||||
|
|
||||||
|
void entry() noexcept override;
|
||||||
|
void exit() noexcept override;
|
||||||
|
|
||||||
|
void update() noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Disconnected(Disconnected const&) = delete;
|
||||||
|
Disconnected& operator=(Disconnected const&) = delete;
|
||||||
|
|
||||||
|
void arm_timer(int interval) noexcept;
|
||||||
|
void disarm_timer() noexcept;
|
||||||
|
|
||||||
|
bool on_timer();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Context {
|
||||||
|
std::unique_ptr<State> state_;
|
||||||
|
waybar::modules::MPD* mpd_module_;
|
||||||
|
|
||||||
|
friend class State;
|
||||||
|
friend class Playing;
|
||||||
|
friend class Paused;
|
||||||
|
friend class Stopped;
|
||||||
|
friend class Disconnected;
|
||||||
|
friend class Idle;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setState(std::unique_ptr<State>&& new_state) noexcept {
|
||||||
|
if (state_.get() != nullptr) {
|
||||||
|
state_->exit();
|
||||||
|
}
|
||||||
|
state_ = std::move(new_state);
|
||||||
|
state_->entry();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_connected() const;
|
||||||
|
bool is_playing() const;
|
||||||
|
bool is_paused() const;
|
||||||
|
bool is_stopped() const;
|
||||||
|
constexpr std::size_t interval() const;
|
||||||
|
void tryConnect() const;
|
||||||
|
void checkErrors(mpd_connection*) const;
|
||||||
|
void do_update();
|
||||||
|
void queryMPD() const;
|
||||||
|
void fetchState() const;
|
||||||
|
constexpr mpd_state state() const;
|
||||||
|
void emit() const;
|
||||||
|
[[nodiscard]] unique_connection& connection();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Context(waybar::modules::MPD* const mpd_module)
|
||||||
|
: state_{std::make_unique<Disconnected>(this)}, mpd_module_{mpd_module} {
|
||||||
|
state_->entry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void play() { state_->play(); }
|
||||||
|
void stop() { state_->stop(); }
|
||||||
|
void pause() { state_->pause(); }
|
||||||
|
void update() noexcept { state_->update(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules::detail
|
24
include/modules/mpd/state.inl.hpp
Normal file
24
include/modules/mpd/state.inl.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; }
|
||||||
|
inline bool Context::is_playing() const { return mpd_module_->playing(); }
|
||||||
|
inline bool Context::is_paused() const { return mpd_module_->paused(); }
|
||||||
|
inline bool Context::is_stopped() const { return mpd_module_->stopped(); }
|
||||||
|
|
||||||
|
constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_.count(); }
|
||||||
|
inline void Context::tryConnect() const { mpd_module_->tryConnect(); }
|
||||||
|
inline unique_connection& Context::connection() { return mpd_module_->connection_; }
|
||||||
|
constexpr inline mpd_state Context::state() const { return mpd_module_->state_; }
|
||||||
|
|
||||||
|
inline void Context::do_update() {
|
||||||
|
mpd_module_->setLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); }
|
||||||
|
inline void Context::queryMPD() const { mpd_module_->queryMPD(); }
|
||||||
|
inline void Context::fetchState() const { mpd_module_->fetchState(); }
|
||||||
|
inline void Context::emit() const { mpd_module_->emit(); }
|
||||||
|
|
||||||
|
} // namespace detail
|
@ -11,6 +11,9 @@
|
|||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
#include "util/rfkill.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
|
|
||||||
@ -71,6 +74,9 @@ class Network : public ALabel {
|
|||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
util::SleeperThread thread_timer_;
|
util::SleeperThread thread_timer_;
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
util::Rfkill rfkill_;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
#include <pulse/volume.h>
|
#include <pulse/volume.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
@ -37,12 +38,14 @@ class Pulseaudio : public ALabel {
|
|||||||
std::string form_factor_;
|
std::string form_factor_;
|
||||||
std::string desc_;
|
std::string desc_;
|
||||||
std::string monitor_;
|
std::string monitor_;
|
||||||
|
std::string default_sink_name_;
|
||||||
// SOURCE
|
// SOURCE
|
||||||
uint32_t source_idx_{0};
|
uint32_t source_idx_{0};
|
||||||
uint16_t source_volume_;
|
uint16_t source_volume_;
|
||||||
bool source_muted_;
|
bool source_muted_;
|
||||||
std::string source_port_name_;
|
std::string source_port_name_;
|
||||||
std::string source_desc_;
|
std::string source_desc_;
|
||||||
|
std::string default_source_name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
31
include/modules/river/tags.hpp
Normal file
31
include/modules/river/tags.hpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtkmm/button.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "AModule.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "river-status-unstable-v1-client-protocol.h"
|
||||||
|
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
namespace waybar::modules::river {
|
||||||
|
|
||||||
|
class Tags : public waybar::AModule {
|
||||||
|
public:
|
||||||
|
Tags(const std::string &, const waybar::Bar &, const Json::Value &);
|
||||||
|
~Tags();
|
||||||
|
|
||||||
|
// Handlers for wayland events
|
||||||
|
void handle_focused_tags(uint32_t tags);
|
||||||
|
void handle_view_tags(struct wl_array *tags);
|
||||||
|
|
||||||
|
struct zriver_status_manager_v1 *status_manager_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const waybar::Bar & bar_;
|
||||||
|
Gtk::Box box_;
|
||||||
|
std::vector<Gtk::Button> buttons_;
|
||||||
|
struct zriver_output_status_v1 *output_status_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace waybar::modules::river */
|
24
include/modules/simpleclock.hpp
Normal file
24
include/modules/simpleclock.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#if FMT_VERSION < 60000
|
||||||
|
#include <fmt/time.h>
|
||||||
|
#else
|
||||||
|
#include <fmt/chrono.h>
|
||||||
|
#endif
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class Clock : public ALabel {
|
||||||
|
public:
|
||||||
|
Clock(const std::string&, const Json::Value&);
|
||||||
|
~Clock() = default;
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
util::SleeperThread thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
30
include/modules/sndio.hpp
Normal file
30
include/modules/sndio.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sndio.h>
|
||||||
|
#include <vector>
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class Sndio : public ALabel {
|
||||||
|
public:
|
||||||
|
Sndio(const std::string&, const Json::Value&);
|
||||||
|
~Sndio();
|
||||||
|
auto update() -> void;
|
||||||
|
auto set_desc(struct sioctl_desc *, unsigned int) -> void;
|
||||||
|
auto put_val(unsigned int, unsigned int) -> void;
|
||||||
|
bool handleScroll(GdkEventScroll *);
|
||||||
|
bool handleToggle(GdkEventButton* const&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto connect_to_sndio() -> void;
|
||||||
|
util::SleeperThread thread_;
|
||||||
|
struct sioctl_hdl *hdl_;
|
||||||
|
std::vector<struct pollfd> pfds_;
|
||||||
|
unsigned int addr_;
|
||||||
|
unsigned int volume_, old_volume_, maxval_;
|
||||||
|
bool muted_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
@ -10,11 +10,6 @@
|
|||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
|
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
|
||||||
#include <sigc++/trackable.h>
|
#include <sigc++/trackable.h>
|
||||||
#ifdef FILESYSTEM_EXPERIMENTAL
|
|
||||||
#include <experimental/filesystem>
|
|
||||||
#else
|
|
||||||
#include <filesystem>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace waybar::modules::SNI {
|
namespace waybar::modules::SNI {
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class Tray : public AModule {
|
|||||||
|
|
||||||
static inline std::size_t nb_hosts_ = 0;
|
static inline std::size_t nb_hosts_ = 0;
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
SNI::Watcher watcher_;
|
SNI::Watcher::singleton watcher_;
|
||||||
SNI::Host host_;
|
SNI::Host host_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,10 +7,24 @@
|
|||||||
namespace waybar::modules::SNI {
|
namespace waybar::modules::SNI {
|
||||||
|
|
||||||
class Watcher {
|
class Watcher {
|
||||||
|
private:
|
||||||
|
Watcher();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Watcher(std::size_t id);
|
|
||||||
~Watcher();
|
~Watcher();
|
||||||
|
|
||||||
|
using singleton = std::shared_ptr<Watcher>;
|
||||||
|
static singleton getInstance() {
|
||||||
|
static std::weak_ptr<Watcher> weak;
|
||||||
|
|
||||||
|
std::shared_ptr<Watcher> strong = weak.lock();
|
||||||
|
if (!strong) {
|
||||||
|
strong = std::shared_ptr<Watcher>(new Watcher());
|
||||||
|
weak = strong;
|
||||||
|
}
|
||||||
|
return strong;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType;
|
typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType;
|
||||||
|
|
||||||
@ -34,7 +48,6 @@ class Watcher {
|
|||||||
void updateRegisteredItems(SnWatcher *obj);
|
void updateRegisteredItems(SnWatcher *obj);
|
||||||
|
|
||||||
uint32_t bus_name_id_;
|
uint32_t bus_name_id_;
|
||||||
uint32_t watcher_id_;
|
|
||||||
GSList * hosts_ = nullptr;
|
GSList * hosts_ = nullptr;
|
||||||
GSList * items_ = nullptr;
|
GSList * items_ = nullptr;
|
||||||
SnWatcher *watcher_ = nullptr;
|
SnWatcher *watcher_ = nullptr;
|
||||||
|
28
include/modules/sway/language.hpp
Normal file
28
include/modules/sway/language.hpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "client.hpp"
|
||||||
|
#include "modules/sway/ipc/client.hpp"
|
||||||
|
#include "util/json.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::sway {
|
||||||
|
|
||||||
|
class Language : public ALabel, public sigc::trackable {
|
||||||
|
public:
|
||||||
|
Language(const std::string& id, const Json::Value& config);
|
||||||
|
~Language() = default;
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onEvent(const struct Ipc::ipc_response&);
|
||||||
|
void onCmd(const struct Ipc::ipc_response&);
|
||||||
|
|
||||||
|
std::string lang_;
|
||||||
|
util::JsonParser parser_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
Ipc ipc_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules::sway
|
@ -1,8 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <gtkmm/button.h>
|
#include <gtkmm/button.h>
|
||||||
#include <gtkmm/label.h>
|
#include <gtkmm/label.h>
|
||||||
|
#include <unordered_map>
|
||||||
#include "AModule.hpp"
|
#include "AModule.hpp"
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
@ -18,6 +20,10 @@ class Workspaces : public AModule, public sigc::trackable {
|
|||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static inline const std::string workspace_switch_cmd_ = "workspace {} \"{}\"";
|
||||||
|
|
||||||
|
static int convertWorkspaceNameToNum(std::string name);
|
||||||
|
|
||||||
void onCmd(const struct Ipc::ipc_response&);
|
void onCmd(const struct Ipc::ipc_response&);
|
||||||
void onEvent(const struct Ipc::ipc_response&);
|
void onEvent(const struct Ipc::ipc_response&);
|
||||||
bool filterButtons();
|
bool filterButtons();
|
||||||
|
@ -14,7 +14,7 @@ class Temperature : public ALabel {
|
|||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::tuple<uint16_t, uint16_t> getTemperature();
|
float getTemperature();
|
||||||
bool isCritical(uint16_t);
|
bool isCritical(uint16_t);
|
||||||
|
|
||||||
std::string file_path_;
|
std::string file_path_;
|
||||||
|
160
include/modules/wlr/taskbar.hpp
Normal file
160
include/modules/wlr/taskbar.hpp
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AModule.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "client.hpp"
|
||||||
|
#include "util/json.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
|
||||||
|
#include <glibmm/refptr.h>
|
||||||
|
|
||||||
|
#include <gtkmm/box.h>
|
||||||
|
#include <gtkmm/button.h>
|
||||||
|
#include <gtkmm/image.h>
|
||||||
|
#include <gtkmm/label.h>
|
||||||
|
#include <gtkmm/icontheme.h>
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace waybar::modules::wlr {
|
||||||
|
|
||||||
|
class Taskbar;
|
||||||
|
|
||||||
|
class Task
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Task(const waybar::Bar&, const Json::Value&, Taskbar*,
|
||||||
|
struct zwlr_foreign_toplevel_handle_v1 *, struct wl_seat*);
|
||||||
|
~Task();
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum State {
|
||||||
|
MAXIMIZED = (1 << 0),
|
||||||
|
MINIMIZED = (1 << 1),
|
||||||
|
ACTIVE = (1 << 2),
|
||||||
|
FULLSCREEN = (1 << 3),
|
||||||
|
INVALID = (1 << 4)
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
static uint32_t global_id;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const waybar::Bar &bar_;
|
||||||
|
const Json::Value &config_;
|
||||||
|
Taskbar *tbar_;
|
||||||
|
struct zwlr_foreign_toplevel_handle_v1 *handle_;
|
||||||
|
struct wl_seat *seat_;
|
||||||
|
|
||||||
|
uint32_t id_;
|
||||||
|
|
||||||
|
Gtk::Button button_;
|
||||||
|
Gtk::Box content_;
|
||||||
|
Gtk::Image icon_;
|
||||||
|
Gtk::Label text_before_;
|
||||||
|
Gtk::Label text_after_;
|
||||||
|
bool button_visible_;
|
||||||
|
|
||||||
|
bool with_icon_;
|
||||||
|
std::string format_before_;
|
||||||
|
std::string format_after_;
|
||||||
|
|
||||||
|
std::string format_tooltip_;
|
||||||
|
|
||||||
|
std::string title_;
|
||||||
|
std::string app_id_;
|
||||||
|
uint32_t state_ = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string repr() const;
|
||||||
|
std::string state_string(bool = false) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* Getter functions */
|
||||||
|
uint32_t id() const { return id_; }
|
||||||
|
std::string title() const { return title_; }
|
||||||
|
std::string app_id() const { return app_id_; }
|
||||||
|
uint32_t state() const { return state_; }
|
||||||
|
bool maximized() const { return state_ & MAXIMIZED; }
|
||||||
|
bool minimized() const { return state_ & MINIMIZED; }
|
||||||
|
bool active() const { return state_ & ACTIVE; }
|
||||||
|
bool fullscreen() const { return state_ & FULLSCREEN; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* Callbacks for the wlr protocol */
|
||||||
|
void handle_title(const char *);
|
||||||
|
void handle_app_id(const char *);
|
||||||
|
void handle_output_enter(struct wl_output *);
|
||||||
|
void handle_output_leave(struct wl_output *);
|
||||||
|
void handle_state(struct wl_array *);
|
||||||
|
void handle_done();
|
||||||
|
void handle_closed();
|
||||||
|
|
||||||
|
/* Callbacks for Gtk events */
|
||||||
|
bool handle_clicked(GdkEventButton *);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool operator==(const Task&) const;
|
||||||
|
bool operator!=(const Task&) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void update();
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* Interaction with the tasks */
|
||||||
|
void maximize(bool);
|
||||||
|
void minimize(bool);
|
||||||
|
void activate();
|
||||||
|
void fullscreen(bool);
|
||||||
|
void close();
|
||||||
|
};
|
||||||
|
|
||||||
|
using TaskPtr = std::unique_ptr<Task>;
|
||||||
|
|
||||||
|
|
||||||
|
class Taskbar : public waybar::AModule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Taskbar(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
|
~Taskbar();
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const waybar::Bar &bar_;
|
||||||
|
Gtk::Box box_;
|
||||||
|
std::vector<TaskPtr> tasks_;
|
||||||
|
|
||||||
|
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes_;
|
||||||
|
|
||||||
|
struct zwlr_foreign_toplevel_manager_v1 *manager_;
|
||||||
|
struct wl_seat *seat_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* Callbacks for global registration */
|
||||||
|
void register_manager(struct wl_registry*, uint32_t name, uint32_t version);
|
||||||
|
void register_seat(struct wl_registry*, uint32_t name, uint32_t version);
|
||||||
|
|
||||||
|
/* Callbacks for the wlr protocol */
|
||||||
|
void handle_toplevel_create(struct zwlr_foreign_toplevel_handle_v1 *);
|
||||||
|
void handle_finished();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void add_button(Gtk::Button &);
|
||||||
|
void move_button(Gtk::Button &, int);
|
||||||
|
void remove_button(Gtk::Button &);
|
||||||
|
void remove_task(uint32_t);
|
||||||
|
|
||||||
|
bool show_output(struct wl_output *) const;
|
||||||
|
bool all_outputs() const;
|
||||||
|
|
||||||
|
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace waybar::modules::wlr */
|
@ -1,9 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <giomm.h>
|
#include <giomm.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#endif
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
#include <sys/procctl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
extern std::mutex reap_mtx;
|
||||||
|
extern std::list<pid_t> reap;
|
||||||
|
|
||||||
namespace waybar::util::command {
|
namespace waybar::util::command {
|
||||||
|
|
||||||
struct res {
|
struct res {
|
||||||
@ -28,20 +41,31 @@ inline std::string read(FILE* fp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline int close(FILE* fp, pid_t pid) {
|
inline int close(FILE* fp, pid_t pid) {
|
||||||
int stat;
|
int stat = -1;
|
||||||
|
pid_t ret;
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
while (waitpid(pid, &stat, 0) == -1) {
|
do {
|
||||||
if (errno != EINTR) {
|
ret = waitpid(pid, &stat, WCONTINUED | WUNTRACED);
|
||||||
stat = 0;
|
|
||||||
|
if (WIFEXITED(stat)) {
|
||||||
|
spdlog::debug("Cmd exited with code {}", WEXITSTATUS(stat));
|
||||||
|
} else if (WIFSIGNALED(stat)) {
|
||||||
|
spdlog::debug("Cmd killed by {}", WTERMSIG(stat));
|
||||||
|
} else if (WIFSTOPPED(stat)) {
|
||||||
|
spdlog::debug("Cmd stopped by {}", WSTOPSIG(stat));
|
||||||
|
} else if (WIFCONTINUED(stat)) {
|
||||||
|
spdlog::debug("Cmd continued");
|
||||||
|
} else if (ret == -1) {
|
||||||
|
spdlog::debug("waitpid failed: {}", strerror(errno));
|
||||||
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
} while (!WIFEXITED(stat) && !WIFSIGNALED(stat));
|
||||||
|
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline FILE* open(const std::string cmd, int& pid) {
|
inline FILE* open(const std::string& cmd, int& pid) {
|
||||||
if (cmd == "") return nullptr;
|
if (cmd == "") return nullptr;
|
||||||
int fd[2];
|
int fd[2];
|
||||||
pipe(fd);
|
pipe(fd);
|
||||||
@ -49,15 +73,33 @@ inline FILE* open(const std::string cmd, int& pid) {
|
|||||||
pid_t child_pid = fork();
|
pid_t child_pid = fork();
|
||||||
|
|
||||||
if (child_pid < 0) {
|
if (child_pid < 0) {
|
||||||
printf("Unable to exec cmd %s, error %s", cmd.c_str(), strerror(errno));
|
spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!child_pid) {
|
if (!child_pid) {
|
||||||
|
int err;
|
||||||
|
sigset_t mask;
|
||||||
|
sigfillset(&mask);
|
||||||
|
// Reset sigmask
|
||||||
|
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
|
||||||
|
if (err != 0) spdlog::error("pthread_sigmask in open failed: {}", strerror(err));
|
||||||
|
// Kill child if Waybar exits
|
||||||
|
int deathsig = SIGTERM;
|
||||||
|
#ifdef __linux__
|
||||||
|
if (prctl(PR_SET_PDEATHSIG, deathsig) != 0) {
|
||||||
|
spdlog::error("prctl(PR_SET_PDEATHSIG) in open failed: {}", strerror(errno));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
if (procctl(P_PID, 0, PROC_PDEATHSIG_CTL, reinterpret_cast<void*>(&deathsig)) == -1) {
|
||||||
|
spdlog::error("procctl(PROC_PDEATHSIG_CTL) in open failed: {}", strerror(errno));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
::close(fd[0]);
|
::close(fd[0]);
|
||||||
dup2(fd[1], 1);
|
dup2(fd[1], 1);
|
||||||
setpgid(child_pid, child_pid);
|
setpgid(child_pid, child_pid);
|
||||||
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
||||||
exit(0);
|
exit(0);
|
||||||
} else {
|
} else {
|
||||||
::close(fd[1]);
|
::close(fd[1]);
|
||||||
@ -66,7 +108,7 @@ inline FILE* open(const std::string cmd, int& pid) {
|
|||||||
return fdopen(fd[0], "r");
|
return fdopen(fd[0], "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline struct res exec(std::string cmd) {
|
inline struct res exec(const std::string& cmd) {
|
||||||
int pid;
|
int pid;
|
||||||
auto fp = command::open(cmd, pid);
|
auto fp = command::open(cmd, pid);
|
||||||
if (!fp) return {-1, ""};
|
if (!fp) return {-1, ""};
|
||||||
@ -75,23 +117,40 @@ inline struct res exec(std::string cmd) {
|
|||||||
return {WEXITSTATUS(stat), output};
|
return {WEXITSTATUS(stat), output};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int32_t forkExec(std::string cmd) {
|
inline struct res execNoRead(const std::string& cmd) {
|
||||||
|
int pid;
|
||||||
|
auto fp = command::open(cmd, pid);
|
||||||
|
if (!fp) return {-1, ""};
|
||||||
|
auto stat = command::close(fp, pid);
|
||||||
|
return {WEXITSTATUS(stat), ""};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int32_t forkExec(const std::string& cmd) {
|
||||||
if (cmd == "") return -1;
|
if (cmd == "") return -1;
|
||||||
|
|
||||||
int32_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
printf("Unable to exec cmd %s, error %s", cmd.c_str(), strerror(errno));
|
spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno));
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Child executes the command
|
// Child executes the command
|
||||||
if (!pid) {
|
if (!pid) {
|
||||||
|
int err;
|
||||||
|
sigset_t mask;
|
||||||
|
sigfillset(&mask);
|
||||||
|
// Reset sigmask
|
||||||
|
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
|
||||||
|
if (err != 0) spdlog::error("pthread_sigmask in forkExec failed: {}", strerror(err));
|
||||||
setpgid(pid, pid);
|
setpgid(pid, pid);
|
||||||
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
||||||
exit(0);
|
exit(0);
|
||||||
} else {
|
} else {
|
||||||
signal(SIGCHLD,SIG_IGN);
|
reap_mtx.lock();
|
||||||
|
reap.push_back(pid);
|
||||||
|
reap_mtx.unlock();
|
||||||
|
spdlog::debug("Added child to reap list: {}", pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pid;
|
return pid;
|
||||||
|
@ -23,7 +23,7 @@ namespace fmt {
|
|||||||
constexpr auto parse(ParseContext& ctx) -> decltype (ctx.begin()) {
|
constexpr auto parse(ParseContext& ctx) -> decltype (ctx.begin()) {
|
||||||
auto it = ctx.begin(), end = ctx.end();
|
auto it = ctx.begin(), end = ctx.end();
|
||||||
if (it != end && *it == ':') ++it;
|
if (it != end && *it == ':') ++it;
|
||||||
if (*it == '>' || *it == '<' || *it == '=') {
|
if (it && (*it == '>' || *it == '<' || *it == '=')) {
|
||||||
spec = *it;
|
spec = *it;
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
26
include/util/rfkill.hpp
Normal file
26
include/util/rfkill.hpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glibmm/iochannel.h>
|
||||||
|
#include <linux/rfkill.h>
|
||||||
|
#include <sigc++/signal.h>
|
||||||
|
#include <sigc++/trackable.h>
|
||||||
|
|
||||||
|
namespace waybar::util {
|
||||||
|
|
||||||
|
class Rfkill : public sigc::trackable {
|
||||||
|
public:
|
||||||
|
Rfkill(enum rfkill_type rfkill_type);
|
||||||
|
~Rfkill();
|
||||||
|
bool getState() const;
|
||||||
|
|
||||||
|
sigc::signal<void(struct rfkill_event&)> on_update;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum rfkill_type rfkill_type_;
|
||||||
|
bool state_ = false;
|
||||||
|
int fd_ = -1;
|
||||||
|
|
||||||
|
bool on_event(Glib::IOCondition cond);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::util
|
@ -59,6 +59,11 @@ class SleeperThread {
|
|||||||
do_run_ = false;
|
do_run_ = false;
|
||||||
}
|
}
|
||||||
condvar_.notify_all();
|
condvar_.notify_all();
|
||||||
|
auto handle = thread_.native_handle();
|
||||||
|
if (handle != 0) {
|
||||||
|
// TODO: find a proper way to terminate thread...
|
||||||
|
pthread_cancel(handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~SleeperThread() {
|
~SleeperThread() {
|
||||||
|
5
include/util/ustring_clen.hpp
Normal file
5
include/util/ustring_clen.hpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <glibmm/ustring.h>
|
||||||
|
|
||||||
|
// calculate column width of ustring
|
||||||
|
int ustring_clen(const Glib::ustring &str);
|
@ -10,29 +10,37 @@ The *backlight* module displays the current backlight level.
|
|||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
*interval* ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
default: 2 ++
|
default: 2 ++
|
||||||
The interval in which information gets polled.
|
The interval in which information gets polled.
|
||||||
|
|
||||||
*format* ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {percent}% ++
|
default: {percent}% ++
|
||||||
The format, how information should be displayed. On {} data gets inserted.
|
The format, how information should be displayed. On {} data gets inserted.
|
||||||
|
|
||||||
*max-length* ++
|
*max-length*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in characters the module should display.
|
The maximum length in characters the module should display.
|
||||||
|
|
||||||
*rotate* ++
|
*min-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The minimum length in characters the module should take up.
|
||||||
|
|
||||||
|
*align*: ++
|
||||||
|
typeof: float ++
|
||||||
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
*states* ++
|
*states*: ++
|
||||||
typeof: array ++
|
typeof: array ++
|
||||||
A number of backlight states which get activated on certain brightness levels.
|
A number of backlight states which get activated on certain brightness levels.
|
||||||
|
|
||||||
*on-click* ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when the module is clicked.
|
Command to execute when the module is clicked.
|
||||||
|
|
||||||
@ -40,19 +48,23 @@ The *backlight* module displays the current backlight level.
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when middle-clicked on the module using mousewheel.
|
Command to execute when middle-clicked on the module using mousewheel.
|
||||||
|
|
||||||
*on-click-right* ++
|
*on-click-right*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when the module is right clicked.
|
Command to execute when the module is right clicked.
|
||||||
|
|
||||||
*on-scroll-up* ++
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when performing a scroll up on the module.
|
Command to execute when performing a scroll up on the module.
|
||||||
|
|
||||||
*on-scroll-down* ++
|
*on-scroll-down*: ++
|
||||||
typeof: string
|
typeof: string
|
||||||
Command to execute when performing a scroll down on the module.
|
Command to execute when performing a scroll down on the module.
|
||||||
|
|
||||||
*smooth-scrolling-threshold* ++
|
*smooth-scrolling-threshold*: ++
|
||||||
typeof: double
|
typeof: double
|
||||||
Threshold to be used when scrolling.
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
@ -10,29 +10,38 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
*bat* ++
|
*bat*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
The battery to monitor, as in /sys/class/power_supply/ instead of auto detect.
|
The battery to monitor, as in /sys/class/power_supply/ instead of auto detect.
|
||||||
|
|
||||||
*adapter* ++
|
*adapter*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
The adapter to monitor, as in /sys/class/power_supply/ instead of auto detect.
|
The adapter to monitor, as in /sys/class/power_supply/ instead of auto detect.
|
||||||
|
|
||||||
*interval* ++
|
*full-at*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Define the max percentage of the battery, for when you've set the battery to stop charging at a lower level to save it. For example, if you've set the battery to stop at 80% that will become the new 100%.
|
||||||
|
|
||||||
|
*design-capacity*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Option to use the battery design capacity instead of it's current maximal capacity.
|
||||||
|
|
||||||
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
default: 60 ++
|
default: 60 ++
|
||||||
The interval in which the information gets polled.
|
The interval in which the information gets polled.
|
||||||
|
|
||||||
*states* ++
|
*states*: ++
|
||||||
typeof: array ++
|
typeof: array ++
|
||||||
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 the time should be displayed.
|
||||||
|
|
||||||
*format-time* ++
|
*format-time*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {H} h {M} min ++
|
default: {H} h {M} min ++
|
||||||
The format, how the time should be displayed.
|
The format, how the time should be displayed.
|
||||||
@ -46,6 +55,14 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
typeof: integer++
|
typeof: integer++
|
||||||
The maximum length in character the module should display.
|
The maximum length in character the module should display.
|
||||||
|
|
||||||
|
*min-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The minimum length in characters the module should take up.
|
||||||
|
|
||||||
|
*align*: ++
|
||||||
|
typeof: float ++
|
||||||
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer++
|
typeof: integer++
|
||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
@ -58,10 +75,14 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when middle-clicked on the module using mousewheel.
|
Command to execute when middle-clicked on the module using mousewheel.
|
||||||
|
|
||||||
*on-click-right* ++
|
*on-click-right*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
@ -70,11 +91,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling down on the module.
|
Command to execute when scrolling down on the module.
|
||||||
|
|
||||||
*smooth-scrolling-threshold* ++
|
*smooth-scrolling-threshold*: ++
|
||||||
typeof: double ++
|
typeof: double ++
|
||||||
Threshold to be used when scrolling.
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
*tooltip* ++
|
*tooltip*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: true ++
|
||||||
Option to disable tooltip on hover.
|
Option to disable tooltip on hover.
|
||||||
@ -83,6 +104,8 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
|
|
||||||
*{capacity}*: Capacity in percentage
|
*{capacity}*: Capacity in percentage
|
||||||
|
|
||||||
|
*{power}*: Power in watts
|
||||||
|
|
||||||
*{icon}*: Icon, as defined in *format-icons*.
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
*{time}*: Estimate of time until full or empty. Note that this is based on the power draw at the last refresh time, not an average.
|
*{time}*: Estimate of time until full or empty. Note that this is based on the power draw at the last refresh time, not an average.
|
||||||
|
96
man/waybar-bluetooth.5.scd
Normal file
96
man/waybar-bluetooth.5.scd
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
waybar-bluetooth(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - bluetooth module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *bluetooth* module displays information about the status of the device's bluetooth device.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *bluetooth*
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: *{icon}* ++
|
||||||
|
The format, how information should be displayed. This format is used when other formats aren't specified.
|
||||||
|
|
||||||
|
*format-icons*: ++
|
||||||
|
typeof: array/object ++
|
||||||
|
Based on the device status, the corresponding icon gets selected. ++
|
||||||
|
The order is *low* to *high*. Or by the state if it is an object.
|
||||||
|
|
||||||
|
*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-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.
|
||||||
|
|
||||||
|
*tooltip-format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
The format, how information should be displayed in the tooltip. This format is used when other formats aren't specified.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{status}*: Status of the bluetooth device.
|
||||||
|
|
||||||
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"bluetooth": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-alt": "bluetooth: {status}",
|
||||||
|
"format-icons": {
|
||||||
|
"enabled": "",
|
||||||
|
"disabled": ""
|
||||||
|
},
|
||||||
|
"tooltip-format": "{}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#bluetooth*
|
@ -26,15 +26,33 @@ The *clock* module displays the current date and time.
|
|||||||
default: inferred local timezone ++
|
default: inferred local timezone ++
|
||||||
The timezone to display the time in, e.g. America/New_York.
|
The timezone to display the time in, e.g. America/New_York.
|
||||||
|
|
||||||
|
*timezones*: ++
|
||||||
|
typeof: list of strings ++
|
||||||
|
A list of timezones to use for time display, changed using the scroll wheel. ++
|
||||||
|
Use "" to represent the system's local timezone. Using %Z in the format or tooltip format is useful to track which time zone is currently displayed.
|
||||||
|
|
||||||
*locale*: ++
|
*locale*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: inferred from current locale ++
|
default: inferred from current locale ++
|
||||||
A locale to be used to display the time. Intended to render times in custom timezones with the proper language and format.
|
A locale to be used to display the time. Intended to render times in custom timezones with the proper language and format.
|
||||||
|
|
||||||
|
*today-format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: <b><u>{}</u></b> ++
|
||||||
|
The format of today's date in the calendar.
|
||||||
|
|
||||||
*max-length*: ++
|
*max-length*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
The maximum length in character the module should display.
|
||||||
|
|
||||||
|
*min-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The minimum length in characters the module should take up.
|
||||||
|
|
||||||
|
*align*: ++
|
||||||
|
typeof: float ++
|
||||||
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
@ -51,6 +69,10 @@ The *clock* module displays the current date and time.
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
|
@ -24,6 +24,14 @@ The *cpu* module displays the current cpu utilization.
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
The maximum length in character the module should display.
|
||||||
|
|
||||||
|
*min-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The minimum length in characters the module should take up.
|
||||||
|
|
||||||
|
*align*: ++
|
||||||
|
typeof: float ++
|
||||||
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
@ -44,6 +52,10 @@ The *cpu* module displays the current cpu utilization.
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
@ -67,6 +79,12 @@ The *cpu* module displays the current cpu utilization.
|
|||||||
|
|
||||||
*{usage}*: Current cpu usage.
|
*{usage}*: Current cpu usage.
|
||||||
|
|
||||||
|
*{avg_frequency}*: Current cpu average frequency (based on all cores) in GHz.
|
||||||
|
|
||||||
|
*{max_frequency}*: Current cpu max frequency (based on the core with the highest frequency) in GHz.
|
||||||
|
|
||||||
|
*{min_frequency}*: Current cpu min frequency (based on the core with the lowest frequency) in GHz.
|
||||||
|
|
||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -22,6 +22,12 @@ Addressed by *custom/<name>*
|
|||||||
The path to a script, which determines if the script in *exec* should be executed.
|
The path to a script, which determines if the script in *exec* should be executed.
|
||||||
*exec* will be executed if the exit code of *exec-if* equals 0.
|
*exec* will be executed if the exit code of *exec-if* equals 0.
|
||||||
|
|
||||||
|
*exec-on-event*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after
|
||||||
|
executing the event command.
|
||||||
|
|
||||||
*return-type*: ++
|
*return-type*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
See *return-type*
|
See *return-type*
|
||||||
@ -33,6 +39,12 @@ Addressed by *custom/<name>*
|
|||||||
You can update it manually with a signal. If no *interval* is defined,
|
You can update it manually with a signal. If no *interval* is defined,
|
||||||
it is assumed that the out script loops it self.
|
it is assumed that the out script loops it self.
|
||||||
|
|
||||||
|
*restart-interval*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The restart interval (in seconds).
|
||||||
|
Can't be used with the *interval* option, so only with continuous scripts.
|
||||||
|
Once the script exit, it'll be re-executed after the *restart-interval*.
|
||||||
|
|
||||||
*signal*: ++
|
*signal*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The signal number used to update the module.
|
The signal number used to update the module.
|
||||||
@ -55,6 +67,14 @@ Addressed by *custom/<name>*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
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*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when clicked on the module.
|
Command to execute when clicked on the module.
|
||||||
@ -67,6 +87,10 @@ Addressed by *custom/<name>*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
|
@ -31,10 +31,22 @@ Addressed by *disk*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
|
*states*: ++
|
||||||
|
typeof: array ++
|
||||||
|
A number of disk utilization states which get activated on certain percentage thresholds (percentage_used). See *waybar-states(5)*.
|
||||||
|
|
||||||
*max-length*: ++
|
*max-length*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
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*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when clicked on the module.
|
Command to execute when clicked on the module.
|
||||||
@ -47,6 +59,10 @@ Addressed by *disk*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
|
@ -27,6 +27,14 @@ screensaving, also known as "presentation mode".
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
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*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when clicked on the module. A click also toggles the state
|
Command to execute when clicked on the module. A click also toggles the state
|
||||||
@ -39,6 +47,10 @@ screensaving, also known as "presentation mode".
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
|
@ -34,6 +34,14 @@ Addressed by *memory*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
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*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when clicked on the module.
|
Command to execute when clicked on the module.
|
||||||
@ -46,6 +54,10 @@ Addressed by *memory*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
|
@ -20,6 +20,10 @@ Addressed by *mpd*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The port MPD listens to. If empty, use the default port.
|
The port MPD listens to. If empty, use the default port.
|
||||||
|
|
||||||
|
*password*: ++
|
||||||
|
typeof: string ++
|
||||||
|
The password required to connect to the MPD server. If empty, no password is sent to MPD.
|
||||||
|
|
||||||
*interval*: ++
|
*interval*: ++
|
||||||
typeof: integer++
|
typeof: integer++
|
||||||
default: 5 ++
|
default: 5 ++
|
||||||
@ -69,6 +73,22 @@ Addressed by *mpd*
|
|||||||
default: "MPD (disconnected)" ++
|
default: "MPD (disconnected)" ++
|
||||||
Tooltip information displayed when the MPD server can't be reached.
|
Tooltip information displayed when the MPD server can't be reached.
|
||||||
|
|
||||||
|
*artist-len*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Maximum length of the Artist tag.
|
||||||
|
|
||||||
|
*album-len*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Maximum length of the Album tag.
|
||||||
|
|
||||||
|
*album-artist-len*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Maximum length of the Album Artist tag.
|
||||||
|
|
||||||
|
*title-len*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Maximum length of the Title tag.
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
@ -77,6 +97,14 @@ Addressed by *mpd*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
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*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when clicked on the module.
|
Command to execute when clicked on the module.
|
||||||
@ -89,6 +117,10 @@ Addressed by *mpd*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
@ -144,6 +176,10 @@ Addressed by *mpd*
|
|||||||
|
|
||||||
*{totalTime}*: The length of the current song. To format as a date/time (see example configuration)
|
*{totalTime}*: The length of the current song. To format as a date/time (see example configuration)
|
||||||
|
|
||||||
|
*{songPosition}*: The position of the current song.
|
||||||
|
|
||||||
|
*{queueLength}*: The length of the current queue.
|
||||||
|
|
||||||
*{stateIcon}*: The icon corresponding the playing or paused status of the player (see *state-icons* option)
|
*{stateIcon}*: The icon corresponding the playing or paused status of the player (see *state-icons* option)
|
||||||
|
|
||||||
*{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option)
|
*{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option)
|
||||||
|
@ -33,7 +33,7 @@ Addressed by *network*
|
|||||||
|
|
||||||
*format-ethernet*: ++
|
*format-ethernet*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
This format is used when a ethernet interface is displayed.
|
This format is used when an ethernet interface is displayed.
|
||||||
|
|
||||||
*format-wifi*: ++
|
*format-wifi*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
@ -47,6 +47,10 @@ Addressed by *network*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
This format is used when the displayed interface is disconnected.
|
This format is used when the displayed interface is disconnected.
|
||||||
|
|
||||||
|
*format-disabled*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed interface is disabled.
|
||||||
|
|
||||||
*format-icons*: ++
|
*format-icons*: ++
|
||||||
typeof: array/object ++
|
typeof: array/object ++
|
||||||
Based on the current signal strength, the corresponding icon gets selected. ++
|
Based on the current signal strength, the corresponding icon gets selected. ++
|
||||||
@ -60,6 +64,14 @@ Addressed by *network*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
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*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when clicked on the module.
|
Command to execute when clicked on the module.
|
||||||
@ -72,6 +84,10 @@ Addressed by *network*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
@ -95,7 +111,7 @@ Addressed by *network*
|
|||||||
|
|
||||||
*tooltip-format-ethernet*: ++
|
*tooltip-format-ethernet*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
This format is used when a ethernet interface is displayed.
|
This format is used when an ethernet interface is displayed.
|
||||||
|
|
||||||
*tooltip-format-wifi*: ++
|
*tooltip-format-wifi*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
@ -105,6 +121,10 @@ Addressed by *network*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
This format is used when the displayed interface is disconnected.
|
This format is used when the displayed interface is disconnected.
|
||||||
|
|
||||||
|
*tooltip-format-disabled*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed interface is disabled.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{ifname}*: Name of the network interface.
|
*{ifname}*: Name of the network interface.
|
||||||
@ -142,6 +162,7 @@ Addressed by *network*
|
|||||||
"format-wifi": "{essid} ({signalStrength}%) ",
|
"format-wifi": "{essid} ({signalStrength}%) ",
|
||||||
"format-ethernet": "{ifname} ",
|
"format-ethernet": "{ifname} ",
|
||||||
"format-disconnected": "", //An empty format will hide the module.
|
"format-disconnected": "", //An empty format will hide the module.
|
||||||
|
"format-disconnected": "",
|
||||||
"tooltip-format": "{ifname}",
|
"tooltip-format": "{ifname}",
|
||||||
"tooltip-format-wifi": "{essid} ({signalStrength}%) ",
|
"tooltip-format-wifi": "{essid} ({signalStrength}%) ",
|
||||||
"tooltip-format-ethernet": "{ifname} ",
|
"tooltip-format-ethernet": "{ifname} ",
|
||||||
@ -154,6 +175,7 @@ Addressed by *network*
|
|||||||
|
|
||||||
- *#network*
|
- *#network*
|
||||||
- *#network.disconnected*
|
- *#network.disconnected*
|
||||||
|
- *#network.disabled*
|
||||||
- *#network.linked*
|
- *#network.linked*
|
||||||
- *#network.ethernet*
|
- *#network.ethernet*
|
||||||
- *#network.wifi*
|
- *#network.wifi*
|
||||||
|
@ -50,6 +50,14 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
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.
|
||||||
|
|
||||||
*scroll-step*: ++
|
*scroll-step*: ++
|
||||||
typeof: float ++
|
typeof: float ++
|
||||||
default: 1.0 ++
|
default: 1.0 ++
|
||||||
@ -67,6 +75,10 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module. This replaces the default behaviour of volume control.
|
Command to execute when scrolling up on the module. This replaces the default behaviour of volume control.
|
||||||
|
42
man/waybar-river-tags.5.scd
Normal file
42
man/waybar-river-tags.5.scd
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
waybar-river-tags(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - river tags module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *tags* module displays the current state of tags in river.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *river/tags*
|
||||||
|
|
||||||
|
*num-tags*: ++
|
||||||
|
typeof: uint ++
|
||||||
|
default: 9 ++
|
||||||
|
The number of tags that should be displayed.
|
||||||
|
|
||||||
|
*tag-labels*: ++
|
||||||
|
typeof: array ++
|
||||||
|
The label to display for each tag.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
|
||||||
|
```
|
||||||
|
"river/tags": {
|
||||||
|
"num-tags": 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#tags button*
|
||||||
|
- *#tags button.occupied*
|
||||||
|
- *#tags button.focused*
|
||||||
|
|
||||||
|
Note that a tag can be both occupied and focused at the same time.
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
waybar(5), river(1)
|
91
man/waybar-sndio.5.scd
Normal file
91
man/waybar-sndio.5.scd
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
waybar-sndio(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - sndio module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *sndio* module displays the current volume reported by sndio(7).
|
||||||
|
|
||||||
|
Additionally, you can control the volume by scrolling *up* or *down* while the
|
||||||
|
cursor is over the module, and clicking on the module toggles mute.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {volume}% ++
|
||||||
|
The format for how information should be 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.
|
||||||
|
|
||||||
|
*scroll-step*: ++
|
||||||
|
typeof: int ++
|
||||||
|
default: 5 ++
|
||||||
|
The speed in which to change the volume when scrolling.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when clicked on the module.
|
||||||
|
This replaces the default behaviour of toggling mute.
|
||||||
|
|
||||||
|
*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.
|
||||||
|
This replaces the default behaviour of volume control.
|
||||||
|
|
||||||
|
*on-scroll-down*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling down on the module.
|
||||||
|
This replaces the default behaviour of volume control.
|
||||||
|
|
||||||
|
*smooth-scrolling-threshold*: ++
|
||||||
|
typeof: double ++
|
||||||
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{volume}*: Volume in percentage.
|
||||||
|
|
||||||
|
*{raw_value}*: Volume as value reported by sndio.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"sndio": {
|
||||||
|
"format": "{raw_value} 🎜",
|
||||||
|
"scroll-step": 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#sndio*
|
||||||
|
- *#sndio.muted*
|
@ -13,7 +13,7 @@ apply a class when the value matches the declared state value.
|
|||||||
Each class gets activated when the current capacity is equal or below the configured *<value>*.
|
Each class gets activated when the current capacity is equal or below the configured *<value>*.
|
||||||
|
|
||||||
- Also each state can have its own *format*.
|
- Also each state can have its own *format*.
|
||||||
Those con be configured via *format-<name>*.
|
Those can be configured via *format-<name>*.
|
||||||
Or if you want to differentiate a bit more even as *format-<status>-<state>*.
|
Or if you want to differentiate a bit more even as *format-<status>-<state>*.
|
||||||
|
|
||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
|
80
man/waybar-sway-language.5.scd
Normal file
80
man/waybar-sway-language.5.scd
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
waybar-sway-language(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - sway language module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *language* module displays the current keyboard layout in Sway
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *sway/language*
|
||||||
|
|
||||||
|
*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.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"sway/language": {
|
||||||
|
"format": "{}",
|
||||||
|
"max-length": 50
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#language*
|
@ -25,6 +25,14 @@ Addressed by *sway/mode*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
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*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when clicked on the module.
|
Command to execute when clicked on the module.
|
||||||
@ -37,6 +45,10 @@ Addressed by *sway/mode*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
|
@ -25,6 +25,14 @@ Addressed by *sway/window*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in character the module should display.
|
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*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when clicked on the module.
|
Command to execute when clicked on the module.
|
||||||
@ -37,6 +45,10 @@ Addressed by *sway/window*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
|
@ -19,7 +19,7 @@ Addressed by *sway/workspaces*
|
|||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {name} ++
|
default: {value} ++
|
||||||
The format, how information should be displayed.
|
The format, how information should be displayed.
|
||||||
|
|
||||||
*format-icons*: ++
|
*format-icons*: ++
|
||||||
@ -31,6 +31,11 @@ Addressed by *sway/workspaces*
|
|||||||
default: false ++
|
default: false ++
|
||||||
If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled.
|
If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled.
|
||||||
|
|
||||||
|
*disable-click*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to false, you can click to change workspace. If set to true this behaviour is disabled.
|
||||||
|
|
||||||
*smooth-scrolling-threshold*: ++
|
*smooth-scrolling-threshold*: ++
|
||||||
typeof: double ++
|
typeof: double ++
|
||||||
Threshold to be used when scrolling.
|
Threshold to be used when scrolling.
|
||||||
@ -60,9 +65,23 @@ Addressed by *sway/workspaces*
|
|||||||
default: empty ++
|
default: empty ++
|
||||||
Lists workspaces that should always be shown, even when non existent
|
Lists workspaces that should always be shown, even when non existent
|
||||||
|
|
||||||
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
*numeric-first*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
Whether to put workspaces starting with numbers before workspaces that do not start with a number.
|
||||||
|
|
||||||
|
*disable-auto-back-and-forth*: ++
|
||||||
|
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.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{name}*: Name of the workspace, as defined by sway.
|
*{value}*: Name of the workspace, as defined by sway.
|
||||||
|
|
||||||
|
*{name}*: Number stripped from workspace value.
|
||||||
|
|
||||||
*{icon}*: Icon, as defined in *format-icons*.
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
@ -75,6 +94,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
|||||||
- *default*: Will be shown, when no string matches is found.
|
- *default*: Will be shown, when no string matches is found.
|
||||||
- *urgent*: Will be shown, when workspace is flagged as urgent
|
- *urgent*: Will be shown, when workspace is flagged as urgent
|
||||||
- *focused*: Will be shown, when workspace is focused
|
- *focused*: Will be shown, when workspace is focused
|
||||||
|
- *persistent*: Will be shown, when workspace is persistent one.
|
||||||
|
|
||||||
# PERSISTENT WORKSPACES
|
# PERSISTENT WORKSPACES
|
||||||
|
|
||||||
@ -100,6 +120,7 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge
|
|||||||
"sway/workspaces": {
|
"sway/workspaces": {
|
||||||
"disable-scroll": true,
|
"disable-scroll": true,
|
||||||
"all-outputs": true,
|
"all-outputs": true,
|
||||||
|
"numeric-first": false,
|
||||||
"format": "{name}: {icon}",
|
"format": "{name}: {icon}",
|
||||||
"format-icons": {
|
"format-icons": {
|
||||||
"1": "",
|
"1": "",
|
||||||
@ -121,3 +142,5 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge
|
|||||||
- *#workspaces button.focused*
|
- *#workspaces button.focused*
|
||||||
- *#workspaces button.urgent*
|
- *#workspaces button.urgent*
|
||||||
- *#workspaces button.persistent*
|
- *#workspaces button.persistent*
|
||||||
|
- *#workspaces button.current_output*
|
||||||
|
- *#workspaces button#sway-workspace-${name}*
|
||||||
|
@ -20,6 +20,14 @@ Addressed by *temperature*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
The temperature path to use, e.g. */sys/class/hwmon/hwmon2/temp1_input* instead of one in */sys/class/thermal/*.
|
The temperature path to use, e.g. */sys/class/hwmon/hwmon2/temp1_input* instead of one in */sys/class/thermal/*.
|
||||||
|
|
||||||
|
*hwmon-path-abs*: ++
|
||||||
|
typeof: string ++
|
||||||
|
The path of the hwmon-directory of the device, e.g. */sys/devices/pci0000:00/0000:00:18.3/hwmon*. (Note that the subdirectory *hwmon/hwmon#*, where *#* is a number is not part of the path!) Has to be used together with *input-filename*.
|
||||||
|
|
||||||
|
*input-filename*: ++
|
||||||
|
typeof: string ++
|
||||||
|
The temperature filename of your *hwmon-path-abs*, e.g. *temp1_input*
|
||||||
|
|
||||||
*critical-threshold*: ++
|
*critical-threshold*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The threshold before it is considered critical (Celsius).
|
The threshold before it is considered critical (Celsius).
|
||||||
@ -36,12 +44,17 @@ Addressed by *temperature*
|
|||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {temperatureC}°C ++
|
default: {temperatureC}°C ++
|
||||||
The format (Celsius/Fahrenheit) in which the temperature should be displayed.
|
The format (Celsius/Fahrenheit/Kelvin) in which the temperature should be displayed.
|
||||||
|
|
||||||
*format-icons*: ++
|
*format-icons*: ++
|
||||||
typeof: array ++
|
typeof: array ++
|
||||||
Based on the current temperature (Celsius) and *critical-threshold* if available, the corresponding icon gets selected. The order is *low* to *high*.
|
Based on the current temperature (Celsius) and *critical-threshold* if available, the corresponding icon gets selected. The order is *low* to *high*.
|
||||||
|
|
||||||
|
*tooltip-format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {temperatureC}°C ++
|
||||||
|
The format for the tooltip
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
@ -50,6 +63,14 @@ Addressed by *temperature*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The maximum length in characters the module should display.
|
The maximum length in characters 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*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you clicked on the module.
|
Command to execute when you clicked on the module.
|
||||||
@ -62,6 +83,10 @@ Addressed by *temperature*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when you right clicked on the module.
|
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*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module.
|
||||||
@ -85,6 +110,8 @@ Addressed by *temperature*
|
|||||||
|
|
||||||
*{temperatureF}*: Temperature in Fahrenheit.
|
*{temperatureF}*: Temperature in Fahrenheit.
|
||||||
|
|
||||||
|
*{temperatureK}*: Temperature in Kelvin.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -20,6 +20,10 @@ Addressed by *tray*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Defines the spacing between the tray icons.
|
Defines the spacing between the tray icons.
|
||||||
|
|
||||||
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
|
112
man/waybar-wlr-taskbar.5.scd
Normal file
112
man/waybar-wlr-taskbar.5.scd
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
waybar-wlr-taskbar(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
wlroots - Taskbar module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *taskbar* module displays the currently open applications. This module requires
|
||||||
|
a compositor that implements the foreign-toplevel-manager interface.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *wlr/taskbar*
|
||||||
|
|
||||||
|
*all-outputs*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to false applications on the waybar's current output will be shown. Otherwise all applications are shown.
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {icon} ++
|
||||||
|
The format, how information should be displayed.
|
||||||
|
|
||||||
|
*icon-theme*: ++
|
||||||
|
typeof: array|string ++
|
||||||
|
The names of the icon-themes that should be used to find an icon. The list will be traversed from left to right. If omitted, the system default will be used.
|
||||||
|
|
||||||
|
*icon-size*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
default: 16 ++
|
||||||
|
The size of the icon.
|
||||||
|
|
||||||
|
*markup*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to true, pango markup will be accepted in format and tooltip-format.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
If set to false no tooltip will be shown.
|
||||||
|
|
||||||
|
*tooltip-format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {title} ++
|
||||||
|
The format, how information in the tooltip should be displayed.
|
||||||
|
|
||||||
|
*active-first*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to true, always reorder the tasks in the taskbar so that the currently active one is first. Otherwise don't reorder.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
The action which should be triggered when clicking on the application button with the left mouse button.
|
||||||
|
|
||||||
|
*on-click-middle*: ++
|
||||||
|
typeof: string ++
|
||||||
|
The action which should be triggered when clicking on the application button with the middle mouse button.
|
||||||
|
|
||||||
|
*on-click-right*: ++
|
||||||
|
typeof: string ++
|
||||||
|
The action which should be triggered when clicking on the application button with the right mouse button.
|
||||||
|
|
||||||
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{icon}*: The icon of the application.
|
||||||
|
|
||||||
|
*{title}*: The title of the application.
|
||||||
|
|
||||||
|
*{app_id}*: The app_id (== application name) of the application.
|
||||||
|
|
||||||
|
*{state}*: The state (minimized, maximized, active, fullscreen) of the application.
|
||||||
|
|
||||||
|
*{short_state}*: The state (minimize == m, maximized == M, active == A, fullscreen == F) represented as one character of the application.
|
||||||
|
|
||||||
|
# CLICK ACTIONS
|
||||||
|
|
||||||
|
*activate*: Bring the application into foreground.
|
||||||
|
*minimize*: Toggle application's minimized state.
|
||||||
|
*minimize-raise*: Bring the application into foreground or toggle its minimized state.
|
||||||
|
*maximize*: Toggle application's maximized state.
|
||||||
|
*fullscreen*: Toggle application's fullscreen state.
|
||||||
|
*close*: Close the application.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"wlr/taskbar": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"icon-size": 14,
|
||||||
|
"icon-theme": "Numix-Circle",
|
||||||
|
"tooltip-format": "{title}",
|
||||||
|
"on-click": "activate",
|
||||||
|
"on-click-middle": "close"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Style
|
||||||
|
|
||||||
|
- *#taskbar*
|
||||||
|
- *#taskbar button*
|
||||||
|
- *#taskbar button.maximized*
|
||||||
|
- *#taskbar button.minimized*
|
||||||
|
- *#taskbar button.active*
|
||||||
|
- *#taskbar button.fullscreen*
|
@ -14,8 +14,9 @@ Valid locations for this file are:
|
|||||||
- *~/.config/waybar/config*
|
- *~/.config/waybar/config*
|
||||||
- *~/waybar/config*
|
- *~/waybar/config*
|
||||||
- */etc/xdg/waybar/config*
|
- */etc/xdg/waybar/config*
|
||||||
|
- *@sysconfdir@/xdg/waybar/config*
|
||||||
|
|
||||||
A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config.
|
A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config
|
||||||
Also a minimal example configuration can be found on the at the bottom of this man page.
|
Also a minimal example configuration can be found on the at the bottom of this man page.
|
||||||
|
|
||||||
# BAR CONFIGURATION
|
# BAR CONFIGURATION
|
||||||
@ -28,7 +29,7 @@ Also a minimal example configuration can be found on the at the bottom of this m
|
|||||||
|
|
||||||
*output* ++
|
*output* ++
|
||||||
typeof: string|array ++
|
typeof: string|array ++
|
||||||
Specifies on which screen this bar will be displayed.
|
Specifies on which screen this bar will be displayed. Exclamation mark(*!*) can be used to exclude specific output.
|
||||||
|
|
||||||
*position* ++
|
*position* ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
@ -185,16 +186,21 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270.
|
|||||||
|
|
||||||
- *waybar-backlight(5)*
|
- *waybar-backlight(5)*
|
||||||
- *waybar-battery(5)*
|
- *waybar-battery(5)*
|
||||||
|
- *waybar-bluetooth(5)*
|
||||||
- *waybar-clock(5)*
|
- *waybar-clock(5)*
|
||||||
- *waybar-cpu(5)*
|
- *waybar-cpu(5)*
|
||||||
- *waybar-custom(5)*
|
- *waybar-custom(5)*
|
||||||
|
- *waybar-disk(5)*
|
||||||
- *waybar-idle-inhibitor(5)*
|
- *waybar-idle-inhibitor(5)*
|
||||||
- *waybar-memory(5)*
|
- *waybar-memory(5)*
|
||||||
- *waybar-mdp(5)*
|
- *waybar-mpd(5)*
|
||||||
- *waybar-network(5)*
|
- *waybar-network(5)*
|
||||||
- *waybar-pulseaudio(5)*
|
- *waybar-pulseaudio(5)*
|
||||||
|
- *waybar-river-tags(5)*
|
||||||
|
- *waybar-states(5)*
|
||||||
- *waybar-sway-mode(5)*
|
- *waybar-sway-mode(5)*
|
||||||
- *waybar-sway-window(5)*
|
- *waybar-sway-window(5)*
|
||||||
- *waybar-sway-workspaces(5)*
|
- *waybar-sway-workspaces(5)*
|
||||||
|
- *waybar-wlr-taskbar(5)*
|
||||||
- *waybar-temperature(5)*
|
- *waybar-temperature(5)*
|
||||||
- *waybar-tray(5)*
|
- *waybar-tray(5)*
|
160
meson.build
160
meson.build
@ -1,7 +1,8 @@
|
|||||||
project(
|
project(
|
||||||
'waybar', 'cpp', 'c',
|
'waybar', 'cpp', 'c',
|
||||||
version: '0.9.1',
|
version: '0.9.6',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
|
meson_version: '>= 0.49.0',
|
||||||
default_options : [
|
default_options : [
|
||||||
'cpp_std=c++17',
|
'cpp_std=c++17',
|
||||||
'buildtype=release',
|
'buildtype=release',
|
||||||
@ -9,28 +10,38 @@ project(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
compiler = meson.get_compiler('cpp')
|
||||||
|
|
||||||
cpp_args = []
|
cpp_args = []
|
||||||
cpp_link_args = []
|
cpp_link_args = []
|
||||||
|
|
||||||
if get_option('libcxx')
|
if get_option('libcxx')
|
||||||
cpp_args += ['-stdlib=libc++']
|
cpp_args += ['-stdlib=libc++']
|
||||||
cpp_link_args += ['-stdlib=libc++', '-lc++abi']
|
cpp_link_args += ['-stdlib=libc++', '-lc++abi']
|
||||||
|
endif
|
||||||
|
|
||||||
|
if compiler.has_link_argument('-lc++fs')
|
||||||
cpp_link_args += ['-lc++fs']
|
cpp_link_args += ['-lc++fs']
|
||||||
else
|
elif compiler.has_link_argument('-lc++experimental')
|
||||||
|
cpp_link_args += ['-lc++experimental']
|
||||||
|
elif compiler.has_link_argument('-lstdc++fs')
|
||||||
cpp_link_args += ['-lstdc++fs']
|
cpp_link_args += ['-lstdc++fs']
|
||||||
endif
|
endif
|
||||||
|
|
||||||
compiler = meson.get_compiler('cpp')
|
git = find_program('git', native: true, required: false)
|
||||||
git = find_program('git', required: false)
|
|
||||||
|
|
||||||
if not git.found()
|
if not git.found()
|
||||||
add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'cpp')
|
add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'cpp')
|
||||||
else
|
else
|
||||||
|
git_path = run_command([git.path(), 'rev-parse', '--show-toplevel']).stdout().strip()
|
||||||
|
if meson.source_root() == git_path
|
||||||
git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip()
|
git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip()
|
||||||
git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip()
|
git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip()
|
||||||
version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch)
|
version = '"@0@ (branch \'@1@\')"'.format(git_commit_hash, git_branch)
|
||||||
add_project_arguments('-DVERSION=@0@'.format(version), language: 'cpp')
|
add_project_arguments('-DVERSION=@0@'.format(version), language: 'cpp')
|
||||||
|
else
|
||||||
|
add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'cpp')
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if not compiler.has_header('filesystem')
|
if not compiler.has_header('filesystem')
|
||||||
@ -61,10 +72,15 @@ endif
|
|||||||
add_global_arguments(cpp_args, language : 'cpp')
|
add_global_arguments(cpp_args, language : 'cpp')
|
||||||
add_global_link_arguments(cpp_link_args, language : 'cpp')
|
add_global_link_arguments(cpp_link_args, language : 'cpp')
|
||||||
|
|
||||||
|
is_linux = host_machine.system() == 'linux'
|
||||||
|
is_dragonfly = host_machine.system() == 'dragonfly'
|
||||||
|
is_freebsd = host_machine.system() == 'freebsd'
|
||||||
|
is_netbsd = host_machine.system() == 'netbsd'
|
||||||
|
is_openbsd = host_machine.system() == 'openbsd'
|
||||||
|
|
||||||
thread_dep = dependency('threads')
|
thread_dep = dependency('threads')
|
||||||
libinput = dependency('libinput')
|
|
||||||
fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep'])
|
fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep'])
|
||||||
spdlog = dependency('spdlog', version : ['>=1.3.1'], fallback : ['spdlog', 'spdlog_dep'])
|
spdlog = dependency('spdlog', version : ['>=1.8.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true'])
|
||||||
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')
|
||||||
@ -73,21 +89,42 @@ dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gt
|
|||||||
giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk'))
|
giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk'))
|
||||||
jsoncpp = dependency('jsoncpp')
|
jsoncpp = dependency('jsoncpp')
|
||||||
sigcpp = dependency('sigc++-2.0')
|
sigcpp = dependency('sigc++-2.0')
|
||||||
|
libepoll = dependency('epoll-shim', required: false)
|
||||||
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'))
|
||||||
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'))
|
||||||
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
||||||
|
|
||||||
|
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||||
|
if libsndio.found()
|
||||||
|
if not compiler.has_function('sioctl_open', prefix: '#include <sndio.h>', dependencies: libsndio)
|
||||||
|
if get_option('sndio').enabled()
|
||||||
|
error('libsndio is too old, required >=1.7.0')
|
||||||
|
else
|
||||||
|
warning('libsndio is too old, required >=1.7.0')
|
||||||
|
libsndio = dependency('', required: false)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
gtk_layer_shell = dependency('gtk-layer-shell-0',
|
gtk_layer_shell = dependency('gtk-layer-shell-0',
|
||||||
required: get_option('gtk-layer-shell'),
|
required: get_option('gtk-layer-shell'),
|
||||||
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
|
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
|
||||||
systemd = dependency('systemd', required: get_option('systemd'))
|
systemd = dependency('systemd', required: get_option('systemd'))
|
||||||
tz_dep = dependency('date', default_options : [ 'use_system_tzdb=true' ], fallback: [ 'date', 'tz_dep' ])
|
tz_dep = dependency('date',
|
||||||
|
required: false,
|
||||||
|
default_options : [ 'use_system_tzdb=true' ],
|
||||||
|
modules : [ 'date::date', 'date::date-tz' ],
|
||||||
|
fallback: [ 'date', 'tz_dep' ])
|
||||||
|
|
||||||
prefix = get_option('prefix')
|
prefix = get_option('prefix')
|
||||||
|
sysconfdir = get_option('sysconfdir')
|
||||||
conf_data = configuration_data()
|
conf_data = configuration_data()
|
||||||
conf_data.set('prefix', prefix)
|
conf_data.set('prefix', prefix)
|
||||||
|
|
||||||
|
add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'cpp')
|
||||||
|
|
||||||
if systemd.found()
|
if systemd.found()
|
||||||
user_units_dir = systemd.get_pkgconfig_variable('systemduserunitdir')
|
user_units_dir = systemd.get_pkgconfig_variable('systemduserunitdir')
|
||||||
|
|
||||||
@ -103,27 +140,54 @@ src_files = files(
|
|||||||
'src/factory.cpp',
|
'src/factory.cpp',
|
||||||
'src/AModule.cpp',
|
'src/AModule.cpp',
|
||||||
'src/ALabel.cpp',
|
'src/ALabel.cpp',
|
||||||
'src/modules/memory.cpp',
|
|
||||||
'src/modules/battery.cpp',
|
|
||||||
'src/modules/clock.cpp',
|
|
||||||
'src/modules/custom.cpp',
|
'src/modules/custom.cpp',
|
||||||
'src/modules/cpu.cpp',
|
|
||||||
'src/modules/disk.cpp',
|
'src/modules/disk.cpp',
|
||||||
'src/modules/idle_inhibitor.cpp',
|
'src/modules/idle_inhibitor.cpp',
|
||||||
'src/modules/temperature.cpp',
|
'src/modules/temperature.cpp',
|
||||||
'src/main.cpp',
|
'src/main.cpp',
|
||||||
'src/bar.cpp',
|
'src/bar.cpp',
|
||||||
'src/client.cpp'
|
'src/client.cpp',
|
||||||
|
'src/util/ustring_clen.cpp'
|
||||||
)
|
)
|
||||||
|
|
||||||
if true # find_program('sway', required : false).found()
|
if is_linux
|
||||||
add_project_arguments('-DHAVE_SWAY', language: 'cpp')
|
add_project_arguments('-DHAVE_CPU_LINUX', language: 'cpp')
|
||||||
src_files += [
|
add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp')
|
||||||
|
src_files += files(
|
||||||
|
'src/modules/battery.cpp',
|
||||||
|
'src/modules/cpu/common.cpp',
|
||||||
|
'src/modules/cpu/linux.cpp',
|
||||||
|
'src/modules/memory/common.cpp',
|
||||||
|
'src/modules/memory/linux.cpp',
|
||||||
|
)
|
||||||
|
elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd
|
||||||
|
add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp')
|
||||||
|
add_project_arguments('-DHAVE_MEMORY_BSD', language: 'cpp')
|
||||||
|
src_files += files(
|
||||||
|
'src/modules/cpu/bsd.cpp',
|
||||||
|
'src/modules/cpu/common.cpp',
|
||||||
|
'src/modules/memory/bsd.cpp',
|
||||||
|
'src/modules/memory/common.cpp',
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
|
||||||
|
add_project_arguments('-DHAVE_SWAY', language: 'cpp')
|
||||||
|
src_files += [
|
||||||
'src/modules/sway/ipc/client.cpp',
|
'src/modules/sway/ipc/client.cpp',
|
||||||
'src/modules/sway/mode.cpp',
|
'src/modules/sway/mode.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'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if true
|
||||||
|
add_project_arguments('-DHAVE_WLR', language: 'cpp')
|
||||||
|
src_files += 'src/modules/wlr/taskbar.cpp'
|
||||||
|
endif
|
||||||
|
|
||||||
|
if true
|
||||||
|
add_project_arguments('-DHAVE_RIVER', language: 'cpp')
|
||||||
|
src_files += 'src/modules/river/tags.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if libnl.found() and libnlgen.found()
|
if libnl.found() and libnlgen.found()
|
||||||
@ -146,20 +210,43 @@ if dbusmenu_gtk.found()
|
|||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if libudev.found()
|
if libudev.found() and (is_linux or libepoll.found())
|
||||||
add_project_arguments('-DHAVE_LIBUDEV', language: 'cpp')
|
add_project_arguments('-DHAVE_LIBUDEV', language: 'cpp')
|
||||||
src_files += 'src/modules/backlight.cpp'
|
src_files += 'src/modules/backlight.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if libmpdclient.found()
|
if libmpdclient.found()
|
||||||
add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp')
|
add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp')
|
||||||
src_files += 'src/modules/mpd.cpp'
|
src_files += 'src/modules/mpd/mpd.cpp'
|
||||||
|
src_files += 'src/modules/mpd/state.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if gtk_layer_shell.found()
|
if gtk_layer_shell.found()
|
||||||
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
|
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if libsndio.found()
|
||||||
|
add_project_arguments('-DHAVE_LIBSNDIO', language: 'cpp')
|
||||||
|
src_files += 'src/modules/sndio.cpp'
|
||||||
|
endif
|
||||||
|
|
||||||
|
if get_option('rfkill').enabled()
|
||||||
|
if is_linux
|
||||||
|
add_project_arguments('-DWANT_RFKILL', language: 'cpp')
|
||||||
|
src_files += files(
|
||||||
|
'src/modules/bluetooth.cpp',
|
||||||
|
'src/util/rfkill.cpp'
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if tz_dep.found()
|
||||||
|
add_project_arguments('-DHAVE_LIBDATE', language: 'cpp')
|
||||||
|
src_files += 'src/modules/clock.cpp'
|
||||||
|
else
|
||||||
|
src_files += 'src/modules/simpleclock.cpp'
|
||||||
|
endif
|
||||||
|
|
||||||
subdir('protocol')
|
subdir('protocol')
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
@ -173,7 +260,6 @@ executable(
|
|||||||
spdlog,
|
spdlog,
|
||||||
sigcpp,
|
sigcpp,
|
||||||
jsoncpp,
|
jsoncpp,
|
||||||
libinput,
|
|
||||||
wayland_cursor,
|
wayland_cursor,
|
||||||
gtkmm,
|
gtkmm,
|
||||||
dbusmenu_gtk,
|
dbusmenu_gtk,
|
||||||
@ -182,8 +268,10 @@ executable(
|
|||||||
libnlgen,
|
libnlgen,
|
||||||
libpulse,
|
libpulse,
|
||||||
libudev,
|
libudev,
|
||||||
|
libepoll,
|
||||||
libmpdclient,
|
libmpdclient,
|
||||||
gtk_layer_shell,
|
gtk_layer_shell,
|
||||||
|
libsndio,
|
||||||
tz_dep
|
tz_dep
|
||||||
],
|
],
|
||||||
include_directories: [include_directories('include')],
|
include_directories: [include_directories('include')],
|
||||||
@ -193,7 +281,7 @@ executable(
|
|||||||
install_data(
|
install_data(
|
||||||
'./resources/config',
|
'./resources/config',
|
||||||
'./resources/style.css',
|
'./resources/style.css',
|
||||||
install_dir: join_paths(get_option('out'), 'etc/xdg/waybar')
|
install_dir: sysconfdir + '/xdg/waybar'
|
||||||
)
|
)
|
||||||
|
|
||||||
scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
|
scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
|
||||||
@ -201,9 +289,20 @@ scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_opti
|
|||||||
if scdoc.found()
|
if scdoc.found()
|
||||||
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
|
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
|
||||||
sh = find_program('sh', native: true)
|
sh = find_program('sh', native: true)
|
||||||
|
|
||||||
|
main_manpage = configure_file(
|
||||||
|
input: 'man/waybar.5.scd.in',
|
||||||
|
output: 'waybar.5.scd',
|
||||||
|
configuration: {
|
||||||
|
'sysconfdir': join_paths(prefix, sysconfdir)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
main_manpage_path = join_paths(meson.build_root(), '@0@'.format(main_manpage))
|
||||||
|
|
||||||
mandir = get_option('mandir')
|
mandir = get_option('mandir')
|
||||||
man_files = [
|
man_files = [
|
||||||
'waybar.5.scd',
|
main_manpage_path,
|
||||||
'waybar-backlight.5.scd',
|
'waybar-backlight.5.scd',
|
||||||
'waybar-battery.5.scd',
|
'waybar-battery.5.scd',
|
||||||
'waybar-clock.5.scd',
|
'waybar-clock.5.scd',
|
||||||
@ -215,22 +314,31 @@ if scdoc.found()
|
|||||||
'waybar-mpd.5.scd',
|
'waybar-mpd.5.scd',
|
||||||
'waybar-network.5.scd',
|
'waybar-network.5.scd',
|
||||||
'waybar-pulseaudio.5.scd',
|
'waybar-pulseaudio.5.scd',
|
||||||
|
'waybar-river-tags.5.scd',
|
||||||
|
'waybar-sway-language.5.scd',
|
||||||
'waybar-sway-mode.5.scd',
|
'waybar-sway-mode.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',
|
||||||
'waybar-tray.5.scd',
|
'waybar-tray.5.scd',
|
||||||
'waybar-states.5.scd',
|
'waybar-states.5.scd',
|
||||||
|
'waybar-wlr-taskbar.5.scd',
|
||||||
|
'waybar-bluetooth.5.scd',
|
||||||
|
'waybar-sndio.5.scd',
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach filename : man_files
|
foreach file : man_files
|
||||||
topic = filename.split('.')[-3].split('/')[-1]
|
path = '@0@'.format(file)
|
||||||
section = filename.split('.')[-2]
|
basename = path.split('/')[-1]
|
||||||
|
|
||||||
|
topic = basename.split('.')[-3]
|
||||||
|
section = basename.split('.')[-2]
|
||||||
output = '@0@.@1@'.format(topic, section)
|
output = '@0@.@1@'.format(topic, section)
|
||||||
|
|
||||||
custom_target(
|
custom_target(
|
||||||
output,
|
output,
|
||||||
input: 'man/@0@'.format(filename),
|
# drops the 'man' if `path` is an absolute path
|
||||||
|
input: join_paths('man', path),
|
||||||
output: output,
|
output: output,
|
||||||
command: [
|
command: [
|
||||||
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
|
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
|
||||||
|
@ -6,5 +6,6 @@ option('systemd', type: 'feature', value: 'auto', description: 'Install systemd
|
|||||||
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')
|
||||||
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
|
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
|
||||||
option('out', type: 'string', value : '/', description: 'output prefix directory')
|
|
||||||
option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support')
|
option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support')
|
||||||
|
option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL')
|
||||||
|
option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio')
|
||||||
|
@ -26,6 +26,8 @@ client_protocols = [
|
|||||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||||
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
|
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
|
||||||
['wlr-layer-shell-unstable-v1.xml'],
|
['wlr-layer-shell-unstable-v1.xml'],
|
||||||
|
['wlr-foreign-toplevel-management-unstable-v1.xml'],
|
||||||
|
['river-status-unstable-v1.xml'],
|
||||||
]
|
]
|
||||||
|
|
||||||
client_protos_src = []
|
client_protos_src = []
|
||||||
|
116
protocol/river-status-unstable-v1.xml
Normal file
116
protocol/river-status-unstable-v1.xml
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="river_status_unstable_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright 2020 Isaac Freund
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="zriver_status_manager_v1" version="1">
|
||||||
|
<description summary="manage river status objects">
|
||||||
|
A global factory for objects that receive status information specific
|
||||||
|
to river. It could be used to implement, for example, a status bar.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the river_status_manager object">
|
||||||
|
This request indicates that the client will not use the
|
||||||
|
river_status_manager object any more. Objects that have been created
|
||||||
|
through this instance are not affected.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_river_output_status">
|
||||||
|
<description summary="create an output status object">
|
||||||
|
This creates a new river_output_status object for the given wl_output.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zriver_output_status_v1"/>
|
||||||
|
<arg name="output" type="object" interface="wl_output"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_river_seat_status">
|
||||||
|
<description summary="create a seat status object">
|
||||||
|
This creates a new river_seat_status object for the given wl_seat.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zriver_seat_status_v1"/>
|
||||||
|
<arg name="seat" type="object" interface="wl_seat"/>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zriver_output_status_v1" version="1">
|
||||||
|
<description summary="track output tags and focus">
|
||||||
|
This interface allows clients to receive information about the current
|
||||||
|
windowing state of an output.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the river_output_status object">
|
||||||
|
This request indicates that the client will not use the
|
||||||
|
river_output_status object any more.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="focused_tags">
|
||||||
|
<description summary="focused tags of the output">
|
||||||
|
Sent once binding the interface and again whenever the tag focus of
|
||||||
|
the output changes.
|
||||||
|
</description>
|
||||||
|
<arg name="tags" type="uint" summary="32-bit bitfield"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="view_tags">
|
||||||
|
<description summary="tag state of an output's views">
|
||||||
|
Sent once on binding the interface and again whenever the tag state
|
||||||
|
of the output changes.
|
||||||
|
</description>
|
||||||
|
<arg name="tags" type="array" summary="array of 32-bit bitfields"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zriver_seat_status_v1" version="1">
|
||||||
|
<description summary="track seat focus">
|
||||||
|
This interface allows clients to receive information about the current
|
||||||
|
focus of a seat.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the river_seat_status object">
|
||||||
|
This request indicates that the client will not use the
|
||||||
|
river_seat_status object any more.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="focused_output">
|
||||||
|
<description summary="the seat focused an output">
|
||||||
|
Sent on binding the interface and again whenever an output gains focus.
|
||||||
|
</description>
|
||||||
|
<arg name="output" type="object" interface="wl_output"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="unfocused_output">
|
||||||
|
<description summary="the seat unfocused an output">
|
||||||
|
Sent whenever an output loses focus.
|
||||||
|
</description>
|
||||||
|
<arg name="output" type="object" interface="wl_output"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="focused_view">
|
||||||
|
<description summary="information on the focused view">
|
||||||
|
Sent once on binding the interface and again whenever the focused
|
||||||
|
view or a property thereof changes. The title may be an empty string
|
||||||
|
if no view is focused or the focused view did not set a title.
|
||||||
|
</description>
|
||||||
|
<arg name="title" type="string" summary="title of the focused view"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
270
protocol/wlr-foreign-toplevel-management-unstable-v1.xml
Normal file
270
protocol/wlr-foreign-toplevel-management-unstable-v1.xml
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="wlr_foreign_toplevel_management_unstable_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2018 Ilia Bozhinov
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute, and sell this
|
||||||
|
software and its documentation for any purpose is hereby granted
|
||||||
|
without fee, provided that the above copyright notice appear in
|
||||||
|
all copies and that both that copyright notice and this permission
|
||||||
|
notice appear in supporting documentation, and that the name of
|
||||||
|
the copyright holders not be used in advertising or publicity
|
||||||
|
pertaining to distribution of the software without specific,
|
||||||
|
written prior permission. The copyright holders make no
|
||||||
|
representations about the suitability of this software for any
|
||||||
|
purpose. It is provided "as is" without express or implied
|
||||||
|
warranty.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||||
|
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="zwlr_foreign_toplevel_manager_v1" version="3">
|
||||||
|
<description summary="list and control opened apps">
|
||||||
|
The purpose of this protocol is to enable the creation of taskbars
|
||||||
|
and docks by providing them with a list of opened applications and
|
||||||
|
letting them request certain actions on them, like maximizing, etc.
|
||||||
|
|
||||||
|
After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
|
||||||
|
toplevel window will be sent via the toplevel event
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<event name="toplevel">
|
||||||
|
<description summary="a toplevel has been created">
|
||||||
|
This event is emitted whenever a new toplevel window is created. It
|
||||||
|
is emitted for all toplevels, regardless of the app that has created
|
||||||
|
them.
|
||||||
|
|
||||||
|
All initial details of the toplevel(title, app_id, states, etc.) will
|
||||||
|
be sent immediately after this event via the corresponding events in
|
||||||
|
zwlr_foreign_toplevel_handle_v1.
|
||||||
|
</description>
|
||||||
|
<arg name="toplevel" type="new_id" interface="zwlr_foreign_toplevel_handle_v1"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="stop">
|
||||||
|
<description summary="stop sending events">
|
||||||
|
Indicates the client no longer wishes to receive events for new toplevels.
|
||||||
|
However the compositor may emit further toplevel_created events, until
|
||||||
|
the finished event is emitted.
|
||||||
|
|
||||||
|
The client must not send any more requests after this one.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="finished">
|
||||||
|
<description summary="the compositor has finished with the toplevel manager">
|
||||||
|
This event indicates that the compositor is done sending events to the
|
||||||
|
zwlr_foreign_toplevel_manager_v1. The server will destroy the object
|
||||||
|
immediately after sending this request, so it will become invalid and
|
||||||
|
the client should free any resources associated with it.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwlr_foreign_toplevel_handle_v1" version="3">
|
||||||
|
<description summary="an opened toplevel">
|
||||||
|
A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
|
||||||
|
window. Each app may have multiple opened toplevels.
|
||||||
|
|
||||||
|
Each toplevel has a list of outputs it is visible on, conveyed to the
|
||||||
|
client with the output_enter and output_leave events.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<event name="title">
|
||||||
|
<description summary="title change">
|
||||||
|
This event is emitted whenever the title of the toplevel changes.
|
||||||
|
</description>
|
||||||
|
<arg name="title" type="string"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="app_id">
|
||||||
|
<description summary="app-id change">
|
||||||
|
This event is emitted whenever the app-id of the toplevel changes.
|
||||||
|
</description>
|
||||||
|
<arg name="app_id" type="string"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="output_enter">
|
||||||
|
<description summary="toplevel entered an output">
|
||||||
|
This event is emitted whenever the toplevel becomes visible on
|
||||||
|
the given output. A toplevel may be visible on multiple outputs.
|
||||||
|
</description>
|
||||||
|
<arg name="output" type="object" interface="wl_output"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="output_leave">
|
||||||
|
<description summary="toplevel left an output">
|
||||||
|
This event is emitted whenever the toplevel stops being visible on
|
||||||
|
the given output. It is guaranteed that an entered-output event
|
||||||
|
with the same output has been emitted before this event.
|
||||||
|
</description>
|
||||||
|
<arg name="output" type="object" interface="wl_output"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="set_maximized">
|
||||||
|
<description summary="requests that the toplevel be maximized">
|
||||||
|
Requests that the toplevel be maximized. If the maximized state actually
|
||||||
|
changes, this will be indicated by the state event.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="unset_maximized">
|
||||||
|
<description summary="requests that the toplevel be unmaximized">
|
||||||
|
Requests that the toplevel be unmaximized. If the maximized state actually
|
||||||
|
changes, this will be indicated by the state event.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_minimized">
|
||||||
|
<description summary="requests that the toplevel be minimized">
|
||||||
|
Requests that the toplevel be minimized. If the minimized state actually
|
||||||
|
changes, this will be indicated by the state event.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="unset_minimized">
|
||||||
|
<description summary="requests that the toplevel be unminimized">
|
||||||
|
Requests that the toplevel be unminimized. If the minimized state actually
|
||||||
|
changes, this will be indicated by the state event.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="activate">
|
||||||
|
<description summary="activate the toplevel">
|
||||||
|
Request that this toplevel be activated on the given seat.
|
||||||
|
There is no guarantee the toplevel will be actually activated.
|
||||||
|
</description>
|
||||||
|
<arg name="seat" type="object" interface="wl_seat"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="state">
|
||||||
|
<description summary="types of states on the toplevel">
|
||||||
|
The different states that a toplevel can have. These have the same meaning
|
||||||
|
as the states with the same names defined in xdg-toplevel
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<entry name="maximized" value="0" summary="the toplevel is maximized"/>
|
||||||
|
<entry name="minimized" value="1" summary="the toplevel is minimized"/>
|
||||||
|
<entry name="activated" value="2" summary="the toplevel is active"/>
|
||||||
|
<entry name="fullscreen" value="3" summary="the toplevel is fullscreen" since="2"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<event name="state">
|
||||||
|
<description summary="the toplevel state changed">
|
||||||
|
This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
|
||||||
|
is created and each time the toplevel state changes, either because of a
|
||||||
|
compositor action or because of a request in this protocol.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<arg name="state" type="array"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="done">
|
||||||
|
<description summary="all information about the toplevel has been sent">
|
||||||
|
This event is sent after all changes in the toplevel state have been
|
||||||
|
sent.
|
||||||
|
|
||||||
|
This allows changes to the zwlr_foreign_toplevel_handle_v1 properties
|
||||||
|
to be seen as atomic, even if they happen via multiple events.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="close">
|
||||||
|
<description summary="request that the toplevel be closed">
|
||||||
|
Send a request to the toplevel to close itself. The compositor would
|
||||||
|
typically use a shell-specific method to carry out this request, for
|
||||||
|
example by sending the xdg_toplevel.close event. However, this gives
|
||||||
|
no guarantees the toplevel will actually be destroyed. If and when
|
||||||
|
this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
|
||||||
|
be emitted.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_rectangle">
|
||||||
|
<description summary="the rectangle which represents the toplevel">
|
||||||
|
The rectangle of the surface specified in this request corresponds to
|
||||||
|
the place where the app using this protocol represents the given toplevel.
|
||||||
|
It can be used by the compositor as a hint for some operations, e.g
|
||||||
|
minimizing. The client is however not required to set this, in which
|
||||||
|
case the compositor is free to decide some default value.
|
||||||
|
|
||||||
|
If the client specifies more than one rectangle, only the last one is
|
||||||
|
considered.
|
||||||
|
|
||||||
|
The dimensions are given in surface-local coordinates.
|
||||||
|
Setting width=height=0 removes the already-set rectangle.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<arg name="surface" type="object" interface="wl_surface"/>
|
||||||
|
<arg name="x" type="int"/>
|
||||||
|
<arg name="y" type="int"/>
|
||||||
|
<arg name="width" type="int"/>
|
||||||
|
<arg name="height" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="invalid_rectangle" value="0"
|
||||||
|
summary="the provided rectangle is invalid"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<event name="closed">
|
||||||
|
<description summary="this toplevel has been destroyed">
|
||||||
|
This event means the toplevel has been destroyed. It is guaranteed there
|
||||||
|
won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
|
||||||
|
toplevel itself becomes inert so any requests will be ignored except the
|
||||||
|
destroy request.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the zwlr_foreign_toplevel_handle_v1 object">
|
||||||
|
Destroys the zwlr_foreign_toplevel_handle_v1 object.
|
||||||
|
|
||||||
|
This request should be called either when the client does not want to
|
||||||
|
use the toplevel anymore or after the closed event to finalize the
|
||||||
|
destruction of the object.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<!-- Version 2 additions -->
|
||||||
|
|
||||||
|
<request name="set_fullscreen" since="2">
|
||||||
|
<description summary="request that the toplevel be fullscreened">
|
||||||
|
Requests that the toplevel be fullscreened on the given output. If the
|
||||||
|
fullscreen state and/or the outputs the toplevel is visible on actually
|
||||||
|
change, this will be indicated by the state and output_enter/leave
|
||||||
|
events.
|
||||||
|
|
||||||
|
The output parameter is only a hint to the compositor. Also, if output
|
||||||
|
is NULL, the compositor should decide which output the toplevel will be
|
||||||
|
fullscreened on, if at all.
|
||||||
|
</description>
|
||||||
|
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="unset_fullscreen" since="2">
|
||||||
|
<description summary="request that the toplevel be unfullscreened">
|
||||||
|
Requests that the toplevel be unfullscreened. If the fullscreen state
|
||||||
|
actually changes, this will be indicated by the state event.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<!-- Version 3 additions -->
|
||||||
|
|
||||||
|
<event name="parent" since="3">
|
||||||
|
<description summary="parent change">
|
||||||
|
This event is emitted whenever the parent of the toplevel changes.
|
||||||
|
|
||||||
|
No event is emitted when the parent handle is destroyed by the client.
|
||||||
|
</description>
|
||||||
|
<arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
@ -25,7 +25,7 @@
|
|||||||
THIS SOFTWARE.
|
THIS SOFTWARE.
|
||||||
</copyright>
|
</copyright>
|
||||||
|
|
||||||
<interface name="zwlr_layer_shell_v1" version="1">
|
<interface name="zwlr_layer_shell_v1" version="3">
|
||||||
<description summary="create surfaces that are layers of the desktop">
|
<description summary="create surfaces that are layers of the desktop">
|
||||||
Clients can use this interface to assign the surface_layer role to
|
Clients can use this interface to assign the surface_layer role to
|
||||||
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||||
@ -82,17 +82,27 @@
|
|||||||
<entry name="top" value="2"/>
|
<entry name="top" value="2"/>
|
||||||
<entry name="overlay" value="3"/>
|
<entry name="overlay" value="3"/>
|
||||||
</enum>
|
</enum>
|
||||||
|
|
||||||
|
<!-- Version 3 additions -->
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor" since="3">
|
||||||
|
<description summary="destroy the layer_shell object">
|
||||||
|
This request indicates that the client will not use the layer_shell
|
||||||
|
object any more. Objects that have been created through this instance
|
||||||
|
are not affected.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
<interface name="zwlr_layer_surface_v1" version="1">
|
<interface name="zwlr_layer_surface_v1" version="3">
|
||||||
<description summary="layer metadata interface">
|
<description summary="layer metadata interface">
|
||||||
An interface that may be implemented by a wl_surface, for surfaces that
|
An interface that may be implemented by a wl_surface, for surfaces that
|
||||||
are designed to be rendered as a layer of a stacked desktop-like
|
are designed to be rendered as a layer of a stacked desktop-like
|
||||||
environment.
|
environment.
|
||||||
|
|
||||||
Layer surface state (size, anchor, exclusive zone, margin, interactivity)
|
Layer surface state (layer, size, anchor, exclusive zone,
|
||||||
is double-buffered, and will be applied at the time wl_surface.commit of
|
margin, interactivity) is double-buffered, and will be applied at the
|
||||||
the corresponding wl_surface is called.
|
time wl_surface.commit of the corresponding wl_surface is called.
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<request name="set_size">
|
<request name="set_size">
|
||||||
@ -115,7 +125,7 @@
|
|||||||
<request name="set_anchor">
|
<request name="set_anchor">
|
||||||
<description summary="configures the anchor point of the surface">
|
<description summary="configures the anchor point of the surface">
|
||||||
Requests that the compositor anchor the surface to the specified edges
|
Requests that the compositor anchor the surface to the specified edges
|
||||||
and corners. If two orthoginal edges are specified (e.g. 'top' and
|
and corners. If two orthogonal edges are specified (e.g. 'top' and
|
||||||
'left'), then the anchor point will be the intersection of the edges
|
'left'), then the anchor point will be the intersection of the edges
|
||||||
(e.g. the top left corner of the output); otherwise the anchor point
|
(e.g. the top left corner of the output); otherwise the anchor point
|
||||||
will be centered on that edge, or in the center if none is specified.
|
will be centered on that edge, or in the center if none is specified.
|
||||||
@ -127,20 +137,25 @@
|
|||||||
|
|
||||||
<request name="set_exclusive_zone">
|
<request name="set_exclusive_zone">
|
||||||
<description summary="configures the exclusive geometry of this surface">
|
<description summary="configures the exclusive geometry of this surface">
|
||||||
Requests that the compositor avoids occluding an area of the surface
|
Requests that the compositor avoids occluding an area with other
|
||||||
with other surfaces. The compositor's use of this information is
|
surfaces. The compositor's use of this information is
|
||||||
implementation-dependent - do not assume that this region will not
|
implementation-dependent - do not assume that this region will not
|
||||||
actually be occluded.
|
actually be occluded.
|
||||||
|
|
||||||
A positive value is only meaningful if the surface is anchored to an
|
A positive value is only meaningful if the surface is anchored to one
|
||||||
edge, rather than a corner. The zone is the number of surface-local
|
edge or an edge and both perpendicular edges. If the surface is not
|
||||||
coordinates from the edge that are considered exclusive.
|
anchored, anchored to only two perpendicular edges (a corner), anchored
|
||||||
|
to only two parallel edges or anchored to all edges, a positive value
|
||||||
|
will be treated the same as zero.
|
||||||
|
|
||||||
|
A positive zone is the distance from the edge in surface-local
|
||||||
|
coordinates to consider exclusive.
|
||||||
|
|
||||||
Surfaces that do not wish to have an exclusive zone may instead specify
|
Surfaces that do not wish to have an exclusive zone may instead specify
|
||||||
how they should interact with surfaces that do. If set to zero, the
|
how they should interact with surfaces that do. If set to zero, the
|
||||||
surface indicates that it would like to be moved to avoid occluding
|
surface indicates that it would like to be moved to avoid occluding
|
||||||
surfaces with a positive excluzive zone. If set to -1, the surface
|
surfaces with a positive exclusive zone. If set to -1, the surface
|
||||||
indicates that it would not like to be moved to accomodate for other
|
indicates that it would not like to be moved to accommodate for other
|
||||||
surfaces, and the compositor should extend it all the way to the edges
|
surfaces, and the compositor should extend it all the way to the edges
|
||||||
it is anchored to.
|
it is anchored to.
|
||||||
|
|
||||||
@ -281,5 +296,16 @@
|
|||||||
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
||||||
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
||||||
</enum>
|
</enum>
|
||||||
|
|
||||||
|
<!-- Version 2 additions -->
|
||||||
|
|
||||||
|
<request name="set_layer" since="2">
|
||||||
|
<description summary="change the layer of the surface">
|
||||||
|
Change the layer that the surface is rendered on.
|
||||||
|
|
||||||
|
Layer is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
|
||||||
|
</request>
|
||||||
</interface>
|
</interface>
|
||||||
</protocol>
|
</protocol>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"layer": "top", // Waybar at top layer
|
// "layer": "top", // Waybar at top layer
|
||||||
// "position": "bottom", // Waybar position (top|bottom|left|right)
|
// "position": "bottom", // Waybar position (top|bottom|left|right)
|
||||||
"height": 30, // Waybar height (to be removed for auto height)
|
"height": 30, // Waybar height (to be removed for auto height)
|
||||||
// "width": 1280, // Waybar width
|
// "width": 1280, // Waybar width
|
||||||
// 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", "custom/media"],
|
||||||
"modules-center": ["sway/window"],
|
"modules-center": ["sway/window"],
|
||||||
"modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "battery", "battery#bat2", "clock", "tray"],
|
"modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "sway/language", "battery", "battery#bat2", "clock", "tray"],
|
||||||
// Modules configuration
|
// Modules configuration
|
||||||
// "sway/workspaces": {
|
// "sway/workspaces": {
|
||||||
// "disable-scroll": true,
|
// "disable-scroll": true,
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"format": "<span style=\"italic\">{}</span>"
|
"format": "<span style=\"italic\">{}</span>"
|
||||||
},
|
},
|
||||||
"mpd": {
|
"mpd": {
|
||||||
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ",
|
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ ",
|
||||||
"format-disconnected": "Disconnected ",
|
"format-disconnected": "Disconnected ",
|
||||||
"format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
|
"format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
|
||||||
"unknown-tag": "N/A",
|
"unknown-tag": "N/A",
|
||||||
|
@ -41,19 +41,19 @@ window#waybar.chromium {
|
|||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border-bottom: 3px solid transparent;
|
/* Use box-shadow instead of border so the text isn't offset */
|
||||||
|
box-shadow: inset 0 -3px transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 */
|
||||||
#workspaces button:hover {
|
#workspaces button:hover {
|
||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
box-shadow: inherit;
|
box-shadow: inset 0 -3px #ffffff;
|
||||||
border-bottom: 3px solid #ffffff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#workspaces button.focused {
|
#workspaces button.focused {
|
||||||
background-color: #64727D;
|
background-color: #64727D;
|
||||||
border-bottom: 3px solid #ffffff;
|
box-shadow: inset 0 -3px #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#workspaces button.urgent {
|
#workspaces button.urgent {
|
||||||
@ -69,6 +69,7 @@ window#waybar.chromium {
|
|||||||
#battery,
|
#battery,
|
||||||
#cpu,
|
#cpu,
|
||||||
#memory,
|
#memory,
|
||||||
|
#disk,
|
||||||
#temperature,
|
#temperature,
|
||||||
#backlight,
|
#backlight,
|
||||||
#network,
|
#network,
|
||||||
@ -83,6 +84,21 @@ window#waybar.chromium {
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#window,
|
||||||
|
#workspaces {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If workspaces is the leftmost module, omit left margin */
|
||||||
|
.modules-left > widget:first-child > #workspaces {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If workspaces is the rightmost module, omit right margin */
|
||||||
|
.modules-right > widget:last-child > #workspaces {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#clock {
|
#clock {
|
||||||
background-color: #64727D;
|
background-color: #64727D;
|
||||||
}
|
}
|
||||||
@ -92,7 +108,7 @@ window#waybar.chromium {
|
|||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#battery.charging {
|
#battery.charging, #battery.plugged {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background-color: #26A65B;
|
background-color: #26A65B;
|
||||||
}
|
}
|
||||||
@ -127,6 +143,10 @@ label:focus {
|
|||||||
background-color: #9b59b6;
|
background-color: #9b59b6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#disk {
|
||||||
|
background-color: #964B00;
|
||||||
|
}
|
||||||
|
|
||||||
#backlight {
|
#backlight {
|
||||||
background-color: #90b1b1;
|
background-color: #90b1b1;
|
||||||
}
|
}
|
||||||
@ -200,3 +220,11 @@ label:focus {
|
|||||||
#mpd.paused {
|
#mpd.paused {
|
||||||
background-color: #51a37a;
|
background-color: #51a37a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#language {
|
||||||
|
background: #00b093;
|
||||||
|
color: #740864;
|
||||||
|
padding: 0 5px;
|
||||||
|
margin: 0 5px;
|
||||||
|
min-width: 16px;
|
||||||
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Highly customizable Wayland bar for Sway and Wlroots based compositors.
|
Description=Highly customizable Wayland bar for Sway and Wlroots based compositors.
|
||||||
Documentation=https://github.com/Alexays/Waybar/wiki/
|
Documentation=https://github.com/Alexays/Waybar/wiki/
|
||||||
PartOf=wayland-session.target
|
PartOf=graphical-session.target
|
||||||
|
After=graphical-session.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=dbus
|
|
||||||
BusName=fr.arouillard.waybar
|
|
||||||
ExecStart=@prefix@/bin/waybar
|
ExecStart=@prefix@/bin/waybar
|
||||||
|
ExecReload=kill -SIGUSR2 $MAINPID
|
||||||
|
Restart=on-failure
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=wayland-session.target
|
WantedBy=graphical-session.target
|
||||||
|
@ -5,8 +5,9 @@
|
|||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id,
|
ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id,
|
||||||
const std::string& format, uint16_t interval, bool ellipsize)
|
const std::string& format, uint16_t interval, bool ellipsize, bool enable_click,
|
||||||
: AModule(config, name, id, config["format-alt"].isString()),
|
bool enable_scroll)
|
||||||
|
: AModule(config, name, id, config["format-alt"].isString() || enable_click, enable_scroll),
|
||||||
format_(config_["format"].isString() ? config_["format"].asString() : format),
|
format_(config_["format"].isString() ? config_["format"].asString() : format),
|
||||||
interval_(config_["interval"] == "once"
|
interval_(config_["interval"] == "once"
|
||||||
? std::chrono::seconds(100000000)
|
? std::chrono::seconds(100000000)
|
||||||
@ -19,19 +20,40 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
|||||||
}
|
}
|
||||||
event_box_.add(label_);
|
event_box_.add(label_);
|
||||||
if (config_["max-length"].isUInt()) {
|
if (config_["max-length"].isUInt()) {
|
||||||
label_.set_max_width_chars(config_["max-length"].asUInt());
|
label_.set_max_width_chars(config_["max-length"].asInt());
|
||||||
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
|
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
|
||||||
|
label_.set_single_line_mode(true);
|
||||||
} else if (ellipsize && label_.get_max_width_chars() == -1) {
|
} else if (ellipsize && label_.get_max_width_chars() == -1) {
|
||||||
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
|
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
|
||||||
|
label_.set_single_line_mode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_["rotate"].isUInt()) {
|
if (config_["min-length"].isUInt()) {
|
||||||
label_.set_angle(config["rotate"].asUInt());
|
label_.set_width_chars(config_["min-length"].asUInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint rotate = 0;
|
||||||
|
|
||||||
|
if (config_["rotate"].isUInt()) {
|
||||||
|
rotate = config["rotate"].asUInt();
|
||||||
|
label_.set_angle(rotate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_["align"].isDouble()) {
|
||||||
|
auto align = config_["align"].asFloat();
|
||||||
|
if (rotate == 90 || rotate == 270) {
|
||||||
|
label_.set_yalign(align);
|
||||||
|
} else {
|
||||||
|
label_.set_xalign(align);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ALabel::update() -> void {
|
auto ALabel::update() -> void {
|
||||||
// Nothing here
|
AModule::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) {
|
std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) {
|
||||||
@ -54,6 +76,29 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ALabel::getIcon(uint16_t percentage, std::vector<std::string>& alts, uint16_t max) {
|
||||||
|
auto format_icons = config_["format-icons"];
|
||||||
|
if (format_icons.isObject()) {
|
||||||
|
std::string _alt = "default";
|
||||||
|
for (const auto& alt : alts) {
|
||||||
|
if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) {
|
||||||
|
_alt = alt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
format_icons = format_icons[_alt];
|
||||||
|
}
|
||||||
|
if (format_icons.isArray()) {
|
||||||
|
auto size = format_icons.size();
|
||||||
|
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1);
|
||||||
|
format_icons = format_icons[idx];
|
||||||
|
}
|
||||||
|
if (format_icons.isString()) {
|
||||||
|
return format_icons.asString();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
|
bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
|
||||||
if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) {
|
if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) {
|
||||||
alt_ = !alt_;
|
alt_ = !alt_;
|
||||||
|
@ -6,7 +6,7 @@ namespace waybar {
|
|||||||
|
|
||||||
AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id,
|
AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id,
|
||||||
bool enable_click, bool enable_scroll)
|
bool enable_click, bool enable_scroll)
|
||||||
: config_(std::move(config)) {
|
: name_(std::move(name)), config_(std::move(config)) {
|
||||||
// configure events' user commands
|
// configure events' user commands
|
||||||
if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
|
if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
|
||||||
config_["on-click-backward"].isString() || config_["on-click-forward"].isString() ||
|
config_["on-click-backward"].isString() || config_["on-click-forward"].isString() ||
|
||||||
@ -23,13 +23,17 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
|||||||
AModule::~AModule() {
|
AModule::~AModule() {
|
||||||
for (const auto& pid : pid_) {
|
for (const auto& pid : pid_) {
|
||||||
if (pid != -1) {
|
if (pid != -1) {
|
||||||
kill(-pid, 9);
|
killpg(pid, SIGTERM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto AModule::update() -> void {
|
auto AModule::update() -> void {
|
||||||
// Nothing here
|
// Run user-provided update handler if configured
|
||||||
|
if (config_["on-update"].isString()) {
|
||||||
|
pid_.push_back(util::command::forkExec(config_["on-update"].asString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AModule::handleToggle(GdkEventButton* const& e) {
|
bool AModule::handleToggle(GdkEventButton* const& e) {
|
||||||
@ -40,9 +44,9 @@ bool AModule::handleToggle(GdkEventButton* const& e) {
|
|||||||
format = config_["on-click-middle"].asString();
|
format = config_["on-click-middle"].asString();
|
||||||
} else if (config_["on-click-right"].isString() && e->button == 3) {
|
} else if (config_["on-click-right"].isString() && e->button == 3) {
|
||||||
format = config_["on-click-right"].asString();
|
format = config_["on-click-right"].asString();
|
||||||
} else if (config_["on-click-forward"].isString() && e->button == 8) {
|
} else if (config_["on-click-backward"].isString() && e->button == 8) {
|
||||||
format = config_["on-click-backward"].asString();
|
format = config_["on-click-backward"].asString();
|
||||||
} else if (config_["on-click-backward"].isString() && e->button == 9) {
|
} else if (config_["on-click-forward"].isString() && e->button == 9) {
|
||||||
format = config_["on-click-forward"].asString();
|
format = config_["on-click-forward"].asString();
|
||||||
}
|
}
|
||||||
if (!format.empty()) {
|
if (!format.empty()) {
|
||||||
|
627
src/bar.cpp
627
src/bar.cpp
@ -2,18 +2,364 @@
|
|||||||
#include <gtk-layer-shell.h>
|
#include <gtk-layer-shell.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
#include "factory.hpp"
|
#include "factory.hpp"
|
||||||
#include <spdlog/spdlog.h>
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
namespace waybar {
|
||||||
|
static constexpr const char* MIN_HEIGHT_MSG =
|
||||||
|
"Requested height: {} exceeds the minimum height: {} required by the modules";
|
||||||
|
|
||||||
|
static constexpr const char* MIN_WIDTH_MSG =
|
||||||
|
"Requested width: {} exceeds the minimum width: {} required by the modules";
|
||||||
|
|
||||||
|
static constexpr const char* BAR_SIZE_MSG = "Bar configured (width: {}, height: {}) for output: {}";
|
||||||
|
|
||||||
|
static constexpr const char* SIZE_DEFINED =
|
||||||
|
"{} size is defined in the config file so it will stay like that";
|
||||||
|
|
||||||
|
#ifdef HAVE_GTK_LAYER_SHELL
|
||||||
|
struct GLSSurfaceImpl : public BarSurface, public sigc::trackable {
|
||||||
|
GLSSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} {
|
||||||
|
output_name_ = output.name;
|
||||||
|
// this has to be executed before GtkWindow.realize
|
||||||
|
gtk_layer_init_for_window(window_.gobj());
|
||||||
|
gtk_layer_set_keyboard_interactivity(window.gobj(), FALSE);
|
||||||
|
gtk_layer_set_monitor(window_.gobj(), output.monitor->gobj());
|
||||||
|
gtk_layer_set_namespace(window_.gobj(), "waybar");
|
||||||
|
|
||||||
|
window.signal_configure_event().connect_notify(
|
||||||
|
sigc::mem_fun(*this, &GLSSurfaceImpl::onConfigure));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setExclusiveZone(bool enable) override {
|
||||||
|
if (enable) {
|
||||||
|
gtk_layer_auto_exclusive_zone_enable(window_.gobj());
|
||||||
|
} else {
|
||||||
|
gtk_layer_set_exclusive_zone(window_.gobj(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMargins(const struct bar_margins& margins) override {
|
||||||
|
gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_LEFT, margins.left);
|
||||||
|
gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, margins.right);
|
||||||
|
gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_TOP, margins.top);
|
||||||
|
gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, margins.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLayer(bar_layer value) override {
|
||||||
|
auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM;
|
||||||
|
if (value == bar_layer::TOP) {
|
||||||
|
layer = GTK_LAYER_SHELL_LAYER_TOP;
|
||||||
|
} else if (value == bar_layer::OVERLAY) {
|
||||||
|
layer = GTK_LAYER_SHELL_LAYER_OVERLAY;
|
||||||
|
}
|
||||||
|
gtk_layer_set_layer(window_.gobj(), layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPosition(const std::string_view& position) override {
|
||||||
|
auto unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM;
|
||||||
|
vertical_ = false;
|
||||||
|
if (position == "bottom") {
|
||||||
|
unanchored = GTK_LAYER_SHELL_EDGE_TOP;
|
||||||
|
} else if (position == "left") {
|
||||||
|
unanchored = GTK_LAYER_SHELL_EDGE_RIGHT;
|
||||||
|
vertical_ = true;
|
||||||
|
} else if (position == "right") {
|
||||||
|
vertical_ = true;
|
||||||
|
unanchored = GTK_LAYER_SHELL_EDGE_LEFT;
|
||||||
|
}
|
||||||
|
for (auto edge : {GTK_LAYER_SHELL_EDGE_LEFT,
|
||||||
|
GTK_LAYER_SHELL_EDGE_RIGHT,
|
||||||
|
GTK_LAYER_SHELL_EDGE_TOP,
|
||||||
|
GTK_LAYER_SHELL_EDGE_BOTTOM}) {
|
||||||
|
gtk_layer_set_anchor(window_.gobj(), edge, unanchored != edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize(uint32_t width, uint32_t height) override {
|
||||||
|
width_ = width;
|
||||||
|
height_ = height;
|
||||||
|
window_.set_size_request(width_, height_);
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Gtk::Window& window_;
|
||||||
|
std::string output_name_;
|
||||||
|
uint32_t width_;
|
||||||
|
uint32_t height_;
|
||||||
|
bool vertical_ = false;
|
||||||
|
|
||||||
|
void onConfigure(GdkEventConfigure* ev) {
|
||||||
|
/*
|
||||||
|
* GTK wants new size for the window.
|
||||||
|
* Actual resizing and management of the exclusve zone is handled within the gtk-layer-shell
|
||||||
|
* code. This event handler only updates stored size of the window and prints some warnings.
|
||||||
|
*
|
||||||
|
* Note: forced resizing to a window smaller than required by GTK would not work with
|
||||||
|
* gtk-layer-shell.
|
||||||
|
*/
|
||||||
|
if (vertical_) {
|
||||||
|
if (width_ > 1 && ev->width > static_cast<int>(width_)) {
|
||||||
|
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (height_ > 1 && ev->height > static_cast<int>(height_)) {
|
||||||
|
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width_ = ev->width;
|
||||||
|
height_ = ev->height;
|
||||||
|
spdlog::info(BAR_SIZE_MSG, width_, height_, output_name_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct RawSurfaceImpl : public BarSurface, public sigc::trackable {
|
||||||
|
RawSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} {
|
||||||
|
output_ = gdk_wayland_monitor_get_wl_output(output.monitor->gobj());
|
||||||
|
output_name_ = output.name;
|
||||||
|
|
||||||
|
window.signal_realize().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::onRealize));
|
||||||
|
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::onMap));
|
||||||
|
window.signal_configure_event().connect_notify(
|
||||||
|
sigc::mem_fun(*this, &RawSurfaceImpl::onConfigure));
|
||||||
|
|
||||||
|
if (window.get_realized()) {
|
||||||
|
onRealize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setExclusiveZone(bool enable) override {
|
||||||
|
exclusive_zone_ = enable;
|
||||||
|
if (layer_surface_) {
|
||||||
|
auto zone = 0;
|
||||||
|
if (enable) {
|
||||||
|
// exclusive zone already includes margin for anchored edge,
|
||||||
|
// only opposite margin should be added
|
||||||
|
if ((anchor_ & VERTICAL_ANCHOR) == VERTICAL_ANCHOR) {
|
||||||
|
zone += width_;
|
||||||
|
zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) ? margins_.right : margins_.left;
|
||||||
|
} else {
|
||||||
|
zone += height_;
|
||||||
|
zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) ? margins_.bottom : margins_.top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spdlog::debug("Set exclusive zone {} for output {}", zone, output_name_);
|
||||||
|
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_.get(), zone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLayer(bar_layer layer) override {
|
||||||
|
layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
|
||||||
|
if (layer == bar_layer::TOP) {
|
||||||
|
layer_ = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
|
||||||
|
} else if (layer == bar_layer::OVERLAY) {
|
||||||
|
layer_ = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
|
||||||
|
}
|
||||||
|
// updating already mapped window
|
||||||
|
if (layer_surface_) {
|
||||||
|
if (zwlr_layer_surface_v1_get_version(layer_surface_.get()) >=
|
||||||
|
ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION) {
|
||||||
|
zwlr_layer_surface_v1_set_layer(layer_surface_.get(), layer_);
|
||||||
|
} else {
|
||||||
|
spdlog::warn("Unable to change layer: layer-shell implementation is too old");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMargins(const struct bar_margins& margins) override {
|
||||||
|
margins_ = margins;
|
||||||
|
// updating already mapped window
|
||||||
|
if (layer_surface_) {
|
||||||
|
zwlr_layer_surface_v1_set_margin(
|
||||||
|
layer_surface_.get(), margins_.top, margins_.right, margins_.bottom, margins_.left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPosition(const std::string_view& position) override {
|
||||||
|
anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
||||||
|
if (position == "bottom") {
|
||||||
|
anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
||||||
|
} else if (position == "left") {
|
||||||
|
anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
|
||||||
|
} else if (position == "right") {
|
||||||
|
anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// updating already mapped window
|
||||||
|
if (layer_surface_) {
|
||||||
|
zwlr_layer_surface_v1_set_anchor(layer_surface_.get(), anchor_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize(uint32_t width, uint32_t height) override {
|
||||||
|
configured_width_ = width_ = width;
|
||||||
|
configured_height_ = height_ = height;
|
||||||
|
// layer_shell.configure handler should update exclusive zone if size changes
|
||||||
|
window_.set_size_request(width, height);
|
||||||
|
};
|
||||||
|
|
||||||
|
void commit() override {
|
||||||
|
if (surface_) {
|
||||||
|
wl_surface_commit(surface_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr static uint8_t VERTICAL_ANCHOR =
|
||||||
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
||||||
|
constexpr static uint8_t HORIZONTAL_ANCHOR =
|
||||||
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
||||||
|
|
||||||
|
template <auto fn>
|
||||||
|
using deleter_fn = std::integral_constant<decltype(fn), fn>;
|
||||||
|
using layer_surface_ptr =
|
||||||
|
std::unique_ptr<zwlr_layer_surface_v1, deleter_fn<zwlr_layer_surface_v1_destroy>>;
|
||||||
|
|
||||||
|
Gtk::Window& window_;
|
||||||
|
std::string output_name_;
|
||||||
|
uint32_t configured_width_ = 0;
|
||||||
|
uint32_t configured_height_ = 0;
|
||||||
|
uint32_t width_ = 0;
|
||||||
|
uint32_t height_ = 0;
|
||||||
|
uint8_t anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
||||||
|
bool exclusive_zone_ = true;
|
||||||
|
struct bar_margins margins_;
|
||||||
|
|
||||||
|
zwlr_layer_shell_v1_layer layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
|
||||||
|
struct wl_output* output_ = nullptr; // owned by GTK
|
||||||
|
struct wl_surface* surface_ = nullptr; // owned by GTK
|
||||||
|
layer_surface_ptr layer_surface_;
|
||||||
|
|
||||||
|
void onRealize() {
|
||||||
|
auto gdk_window = window_.get_window()->gobj();
|
||||||
|
gdk_wayland_window_set_use_custom_surface(gdk_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMap(GdkEventAny* ev) {
|
||||||
|
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
|
||||||
|
.configure = onSurfaceConfigure,
|
||||||
|
.closed = onSurfaceClosed,
|
||||||
|
};
|
||||||
|
auto client = Client::inst();
|
||||||
|
auto gdk_window = window_.get_window()->gobj();
|
||||||
|
surface_ = gdk_wayland_window_get_wl_surface(gdk_window);
|
||||||
|
|
||||||
|
layer_surface_.reset(zwlr_layer_shell_v1_get_layer_surface(
|
||||||
|
client->layer_shell, surface_, output_, layer_, "waybar"));
|
||||||
|
|
||||||
|
zwlr_layer_surface_v1_add_listener(layer_surface_.get(), &layer_surface_listener, this);
|
||||||
|
zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_.get(), false);
|
||||||
|
zwlr_layer_surface_v1_set_anchor(layer_surface_.get(), anchor_);
|
||||||
|
zwlr_layer_surface_v1_set_margin(
|
||||||
|
layer_surface_.get(), margins_.top, margins_.right, margins_.bottom, margins_.left);
|
||||||
|
|
||||||
|
setSurfaceSize(width_, height_);
|
||||||
|
setExclusiveZone(exclusive_zone_);
|
||||||
|
|
||||||
|
commit();
|
||||||
|
wl_display_roundtrip(client->wl_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onConfigure(GdkEventConfigure* ev) {
|
||||||
|
/*
|
||||||
|
* GTK wants new size for the window.
|
||||||
|
*
|
||||||
|
* Prefer configured size if it's non-default.
|
||||||
|
* If the size is not set and the window is smaller than requested by GTK, request resize from
|
||||||
|
* layer surface.
|
||||||
|
*/
|
||||||
|
auto tmp_height = height_;
|
||||||
|
auto tmp_width = width_;
|
||||||
|
if (ev->height > static_cast<int>(height_)) {
|
||||||
|
// Default minimal value
|
||||||
|
if (height_ > 1) {
|
||||||
|
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
|
||||||
|
}
|
||||||
|
if (configured_height_ > 1) {
|
||||||
|
spdlog::info(SIZE_DEFINED, "Height");
|
||||||
|
} else {
|
||||||
|
tmp_height = ev->height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ev->width > static_cast<int>(width_)) {
|
||||||
|
// Default minimal value
|
||||||
|
if (width_ > 1) {
|
||||||
|
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
|
||||||
|
}
|
||||||
|
if (configured_width_ > 1) {
|
||||||
|
spdlog::info(SIZE_DEFINED, "Width");
|
||||||
|
} else {
|
||||||
|
tmp_width = ev->width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tmp_width != width_ || tmp_height != height_) {
|
||||||
|
setSurfaceSize(tmp_width, tmp_height);
|
||||||
|
commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSurfaceSize(uint32_t width, uint32_t height) {
|
||||||
|
/* If the client is anchored to two opposite edges, layer_surface.configure will return
|
||||||
|
* size without margins for the axis.
|
||||||
|
* layer_surface.set_size, however, expects size with margins for the anchored axis.
|
||||||
|
* This is not specified by wlr-layer-shell and based on actual behavior of sway.
|
||||||
|
*
|
||||||
|
* If the size for unanchored axis is not set (0), change request to 1 to avoid automatic
|
||||||
|
* assignment by the compositor.
|
||||||
|
*/
|
||||||
|
if ((anchor_ & VERTICAL_ANCHOR) == VERTICAL_ANCHOR) {
|
||||||
|
width = width > 0 ? width : 1;
|
||||||
|
if (height > 1) {
|
||||||
|
height += margins_.top + margins_.bottom;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
height = height > 0 ? height : 1;
|
||||||
|
if (width > 1) {
|
||||||
|
width += margins_.right + margins_.left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spdlog::debug("Set surface size {}x{} for output {}", width, height, output_name_);
|
||||||
|
zwlr_layer_surface_v1_set_size(layer_surface_.get(), width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onSurfaceConfigure(void* data, struct zwlr_layer_surface_v1* surface, uint32_t serial,
|
||||||
|
uint32_t width, uint32_t height) {
|
||||||
|
auto o = static_cast<RawSurfaceImpl*>(data);
|
||||||
|
if (width != o->width_ || height != o->height_) {
|
||||||
|
o->width_ = width;
|
||||||
|
o->height_ = height;
|
||||||
|
o->window_.set_size_request(o->width_, o->height_);
|
||||||
|
o->window_.resize(o->width_, o->height_);
|
||||||
|
o->setExclusiveZone(o->exclusive_zone_);
|
||||||
|
spdlog::info(BAR_SIZE_MSG,
|
||||||
|
o->width_ == 1 ? "auto" : std::to_string(o->width_),
|
||||||
|
o->height_ == 1 ? "auto" : std::to_string(o->height_),
|
||||||
|
o->output_name_);
|
||||||
|
o->commit();
|
||||||
|
}
|
||||||
|
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onSurfaceClosed(void* data, struct zwlr_layer_surface_v1* /* surface */) {
|
||||||
|
auto o = static_cast<RawSurfaceImpl*>(data);
|
||||||
|
o->layer_surface_.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace waybar
|
||||||
|
|
||||||
waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||||
: output(w_output),
|
: output(w_output),
|
||||||
config(w_config),
|
config(w_config),
|
||||||
window{Gtk::WindowType::WINDOW_TOPLEVEL},
|
window{Gtk::WindowType::WINDOW_TOPLEVEL},
|
||||||
surface(nullptr),
|
layer_{bar_layer::BOTTOM},
|
||||||
layer_surface_(nullptr),
|
|
||||||
anchor_(ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP),
|
|
||||||
left_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
left_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||||
center_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
center_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||||
right_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
right_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||||
@ -25,26 +371,15 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
window.get_style_context()->add_class(config["name"].asString());
|
window.get_style_context()->add_class(config["name"].asString());
|
||||||
window.get_style_context()->add_class(config["position"].asString());
|
window.get_style_context()->add_class(config["position"].asString());
|
||||||
|
|
||||||
if (config["position"] == "right" || config["position"] == "left") {
|
if (config["layer"] == "top") {
|
||||||
height_ = 0;
|
layer_ = bar_layer::TOP;
|
||||||
width_ = 1;
|
} else if (config["layer"] == "overlay") {
|
||||||
|
layer_ = bar_layer::OVERLAY;
|
||||||
}
|
}
|
||||||
height_ = config["height"].isUInt() ? config["height"].asUInt() : height_;
|
|
||||||
width_ = config["width"].isUInt() ? config["width"].asUInt() : width_;
|
|
||||||
|
|
||||||
if (config["position"] == "bottom") {
|
auto position = config["position"].asString();
|
||||||
anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
|
||||||
} else if (config["position"] == "left") {
|
if (position == "right" || position == "left") {
|
||||||
anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
|
|
||||||
} else if (config["position"] == "right") {
|
|
||||||
anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
|
||||||
}
|
|
||||||
if (anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ||
|
|
||||||
anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) {
|
|
||||||
anchor_ |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
|
||||||
} else if (anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT ||
|
|
||||||
anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) {
|
|
||||||
anchor_ |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
|
||||||
left_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
|
left_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
|
||||||
center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
|
center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
|
||||||
right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
|
right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
|
||||||
@ -52,6 +387,15 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
vertical = true;
|
vertical = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
left_.get_style_context()->add_class("modules-left");
|
||||||
|
center_.get_style_context()->add_class("modules-center");
|
||||||
|
right_.get_style_context()->add_class("modules-right");
|
||||||
|
|
||||||
|
uint32_t height = config["height"].isUInt() ? config["height"].asUInt() : 0;
|
||||||
|
uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0;
|
||||||
|
|
||||||
|
struct bar_margins margins_;
|
||||||
|
|
||||||
if (config["margin-top"].isInt() || config["margin-right"].isInt() ||
|
if (config["margin-top"].isInt() || config["margin-right"].isInt() ||
|
||||||
config["margin-bottom"].isInt() || config["margin-left"].isInt()) {
|
config["margin-bottom"].isInt() || config["margin-left"].isInt()) {
|
||||||
margins_ = {
|
margins_ = {
|
||||||
@ -98,165 +442,62 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_GTK_LAYER_SHELL
|
#ifdef HAVE_GTK_LAYER_SHELL
|
||||||
use_gls_ = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true;
|
bool use_gls = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true;
|
||||||
if (use_gls_) {
|
if (use_gls) {
|
||||||
initGtkLayerShell();
|
surface_impl_ = std::make_unique<GLSSurfaceImpl>(window, *output);
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize));
|
|
||||||
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
|
||||||
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
|
|
||||||
window.set_size_request(width_, height_);
|
|
||||||
setupWidgets();
|
|
||||||
|
|
||||||
if (window.get_realized()) {
|
|
||||||
onRealize();
|
|
||||||
}
|
|
||||||
window.show_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
|
|
||||||
auto tmp_height = height_;
|
|
||||||
auto tmp_width = width_;
|
|
||||||
if (ev->height > static_cast<int>(height_)) {
|
|
||||||
// Default minimal value
|
|
||||||
if (height_ != 1) {
|
|
||||||
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
|
|
||||||
}
|
|
||||||
if (config["height"].isUInt()) {
|
|
||||||
spdlog::info(SIZE_DEFINED, "Height");
|
|
||||||
} else {
|
|
||||||
tmp_height = ev->height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ev->width > static_cast<int>(width_)) {
|
|
||||||
// Default minimal value
|
|
||||||
if (width_ != 1) {
|
|
||||||
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
|
|
||||||
}
|
|
||||||
if (config["width"].isUInt()) {
|
|
||||||
spdlog::info(SIZE_DEFINED, "Width");
|
|
||||||
} else {
|
|
||||||
tmp_width = ev->width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (use_gls_) {
|
|
||||||
width_ = tmp_width;
|
|
||||||
height_ = tmp_height;
|
|
||||||
spdlog::debug("Set surface size {}x{} for output {}", width_, height_, output->name);
|
|
||||||
setExclusiveZone(tmp_width, tmp_height);
|
|
||||||
} else if (tmp_width != width_ || tmp_height != height_) {
|
|
||||||
setSurfaceSize(tmp_width, tmp_height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_GTK_LAYER_SHELL
|
|
||||||
void waybar::Bar::initGtkLayerShell() {
|
|
||||||
auto gtk_window = window.gobj();
|
|
||||||
// this has to be executed before GtkWindow.realize
|
|
||||||
gtk_layer_init_for_window(gtk_window);
|
|
||||||
gtk_layer_set_keyboard_interactivity(gtk_window, FALSE);
|
|
||||||
auto layer = config["layer"] == "top" ? GTK_LAYER_SHELL_LAYER_TOP : GTK_LAYER_SHELL_LAYER_BOTTOM;
|
|
||||||
gtk_layer_set_layer(gtk_window, layer);
|
|
||||||
gtk_layer_set_monitor(gtk_window, output->monitor->gobj());
|
|
||||||
gtk_layer_set_namespace(gtk_window, "waybar");
|
|
||||||
|
|
||||||
gtk_layer_set_anchor(
|
|
||||||
gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
|
|
||||||
gtk_layer_set_anchor(
|
|
||||||
gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
|
|
||||||
gtk_layer_set_anchor(
|
|
||||||
gtk_window, GTK_LAYER_SHELL_EDGE_TOP, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP);
|
|
||||||
gtk_layer_set_anchor(
|
|
||||||
gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
|
|
||||||
|
|
||||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, margins_.left);
|
|
||||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right);
|
|
||||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_TOP, margins_.top);
|
|
||||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, margins_.bottom);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void waybar::Bar::onRealize() {
|
|
||||||
auto gdk_window = window.get_window()->gobj();
|
|
||||||
gdk_wayland_window_set_use_custom_surface(gdk_window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::Bar::onMap(GdkEventAny* ev) {
|
|
||||||
auto gdk_window = window.get_window()->gobj();
|
|
||||||
surface = gdk_wayland_window_get_wl_surface(gdk_window);
|
|
||||||
|
|
||||||
if (use_gls_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto client = waybar::Client::inst();
|
|
||||||
// owned by output->monitor; no need to destroy
|
|
||||||
auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj());
|
|
||||||
auto layer =
|
|
||||||
config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
|
|
||||||
layer_surface_ = zwlr_layer_shell_v1_get_layer_surface(
|
|
||||||
client->layer_shell, surface, wl_output, layer, "waybar");
|
|
||||||
|
|
||||||
zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_, false);
|
|
||||||
zwlr_layer_surface_v1_set_anchor(layer_surface_, anchor_);
|
|
||||||
zwlr_layer_surface_v1_set_margin(
|
|
||||||
layer_surface_, margins_.top, margins_.right, margins_.bottom, margins_.left);
|
|
||||||
setSurfaceSize(width_, height_);
|
|
||||||
setExclusiveZone(width_, height_);
|
|
||||||
|
|
||||||
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
|
|
||||||
.configure = layerSurfaceHandleConfigure,
|
|
||||||
.closed = layerSurfaceHandleClosed,
|
|
||||||
};
|
|
||||||
zwlr_layer_surface_v1_add_listener(layer_surface_, &layer_surface_listener, this);
|
|
||||||
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
wl_display_roundtrip(client->wl_display);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::Bar::setExclusiveZone(uint32_t width, uint32_t height) {
|
|
||||||
auto zone = 0;
|
|
||||||
if (visible) {
|
|
||||||
// exclusive zone already includes margin for anchored edge,
|
|
||||||
// only opposite margin should be added
|
|
||||||
if (vertical) {
|
|
||||||
zone += width;
|
|
||||||
zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) ? margins_.right : margins_.left;
|
|
||||||
} else {
|
|
||||||
zone += height;
|
|
||||||
zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) ? margins_.bottom : margins_.top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spdlog::debug("Set exclusive zone {} for output {}", zone, output->name);
|
|
||||||
|
|
||||||
#ifdef HAVE_GTK_LAYER_SHELL
|
|
||||||
if (use_gls_) {
|
|
||||||
gtk_layer_set_exclusive_zone(window.gobj(), zone);
|
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_, zone);
|
surface_impl_ = std::make_unique<RawSurfaceImpl>(window, *output);
|
||||||
|
}
|
||||||
|
|
||||||
|
surface_impl_->setLayer(layer_);
|
||||||
|
surface_impl_->setExclusiveZone(true);
|
||||||
|
surface_impl_->setMargins(margins_);
|
||||||
|
surface_impl_->setPosition(position);
|
||||||
|
surface_impl_->setSize(width, height);
|
||||||
|
|
||||||
|
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
||||||
|
|
||||||
|
setupWidgets();
|
||||||
|
window.show_all();
|
||||||
|
|
||||||
|
if (spdlog::should_log(spdlog::level::debug)) {
|
||||||
|
// Unfortunately, this function isn't in the C++ bindings, so we have to call the C version.
|
||||||
|
char* gtk_tree = gtk_style_context_to_string(
|
||||||
|
window.get_style_context()->gobj(),
|
||||||
|
(GtkStyleContextPrintFlags)(GTK_STYLE_CONTEXT_PRINT_RECURSE |
|
||||||
|
GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE));
|
||||||
|
spdlog::debug("GTK widget tree:\n{}", gtk_tree);
|
||||||
|
g_free(gtk_tree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::Bar::setSurfaceSize(uint32_t width, uint32_t height) {
|
void waybar::Bar::onMap(GdkEventAny*) {
|
||||||
/* If the client is anchored to two opposite edges, layer_surface.configure will return
|
/*
|
||||||
* size without margins for the axis.
|
* Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor).
|
||||||
* layer_surface.set_size, however, expects size with margins for the anchored axis.
|
|
||||||
* This is not specified by wlr-layer-shell and based on actual behavior of sway.
|
|
||||||
*/
|
*/
|
||||||
if (vertical && height > 1) {
|
auto gdk_window = window.get_window()->gobj();
|
||||||
height += margins_.top + margins_.bottom;
|
surface = gdk_wayland_window_get_wl_surface(gdk_window);
|
||||||
}
|
|
||||||
if (!vertical && width > 1) {
|
|
||||||
width += margins_.right + margins_.left;
|
|
||||||
}
|
|
||||||
spdlog::debug("Set surface size {}x{} for output {}", width, height, output->name);
|
|
||||||
zwlr_layer_surface_v1_set_size(layer_surface_, width, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::Bar::setVisible(bool value) {
|
||||||
|
visible = value;
|
||||||
|
if (!visible) {
|
||||||
|
window.get_style_context()->add_class("hidden");
|
||||||
|
window.set_opacity(0);
|
||||||
|
surface_impl_->setLayer(bar_layer::BOTTOM);
|
||||||
|
} else {
|
||||||
|
window.get_style_context()->remove_class("hidden");
|
||||||
|
window.set_opacity(1);
|
||||||
|
surface_impl_->setLayer(layer_);
|
||||||
|
}
|
||||||
|
surface_impl_->setExclusiveZone(visible);
|
||||||
|
surface_impl_->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::Bar::toggle() { setVisible(!visible); }
|
||||||
|
|
||||||
// Converting string to button code rn as to avoid doing it later
|
// Converting string to button code rn as to avoid doing it later
|
||||||
void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) {
|
void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) {
|
||||||
if (config.isMember(module_name)) {
|
if (config.isMember(module_name)) {
|
||||||
@ -318,48 +559,6 @@ void waybar::Bar::handleSignal(int signal) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surface_v1* surface,
|
|
||||||
uint32_t serial, uint32_t width, uint32_t height) {
|
|
||||||
auto o = static_cast<waybar::Bar*>(data);
|
|
||||||
if (width != o->width_ || height != o->height_) {
|
|
||||||
o->width_ = width;
|
|
||||||
o->height_ = height;
|
|
||||||
o->window.set_size_request(o->width_, o->height_);
|
|
||||||
o->window.resize(o->width_, o->height_);
|
|
||||||
o->setExclusiveZone(width, height);
|
|
||||||
spdlog::info(BAR_SIZE_MSG,
|
|
||||||
o->width_ == 1 ? "auto" : std::to_string(o->width_),
|
|
||||||
o->height_ == 1 ? "auto" : std::to_string(o->height_),
|
|
||||||
o->output->name);
|
|
||||||
wl_surface_commit(o->surface);
|
|
||||||
}
|
|
||||||
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface_v1* /*surface*/) {
|
|
||||||
auto o = static_cast<waybar::Bar*>(data);
|
|
||||||
if (o->layer_surface_) {
|
|
||||||
zwlr_layer_surface_v1_destroy(o->layer_surface_);
|
|
||||||
o->layer_surface_ = nullptr;
|
|
||||||
}
|
|
||||||
o->modules_left_.clear();
|
|
||||||
o->modules_center_.clear();
|
|
||||||
o->modules_right_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto waybar::Bar::toggle() -> void {
|
|
||||||
visible = !visible;
|
|
||||||
if (!visible) {
|
|
||||||
window.get_style_context()->add_class("hidden");
|
|
||||||
window.set_opacity(0);
|
|
||||||
} else {
|
|
||||||
window.get_style_context()->remove_class("hidden");
|
|
||||||
window.set_opacity(1);
|
|
||||||
}
|
|
||||||
setExclusiveZone(width_, height_);
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::Bar::getModules(const Factory& factory, const std::string& pos) {
|
void waybar::Bar::getModules(const Factory& factory, const std::string& pos) {
|
||||||
if (config[pos].isArray()) {
|
if (config[pos].isArray()) {
|
||||||
for (const auto& name : config[pos]) {
|
for (const auto& name : config[pos]) {
|
||||||
|
122
src/client.cpp
122
src/client.cpp
@ -1,10 +1,15 @@
|
|||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
|
|
||||||
#include <fmt/ostream.h>
|
#include <fmt/ostream.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
#include "util/clara.hpp"
|
#include "util/clara.hpp"
|
||||||
#include "util/json.hpp"
|
#include "util/json.hpp"
|
||||||
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
waybar::Client *waybar::Client::inst() {
|
waybar::Client *waybar::Client::inst() {
|
||||||
static auto c = new Client();
|
static auto c = new Client();
|
||||||
@ -32,6 +37,8 @@ void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint
|
|||||||
const char *interface, uint32_t version) {
|
const char *interface, uint32_t version) {
|
||||||
auto client = static_cast<Client *>(data);
|
auto client = static_cast<Client *>(data);
|
||||||
if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||||
|
// limit version to a highest supported by the client protocol file
|
||||||
|
version = std::min<uint32_t>(version, zwlr_layer_shell_v1_interface.version);
|
||||||
client->layer_shell = static_cast<struct zwlr_layer_shell_v1 *>(
|
client->layer_shell = static_cast<struct zwlr_layer_shell_v1 *>(
|
||||||
wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version));
|
wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version));
|
||||||
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
|
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
|
||||||
@ -53,9 +60,9 @@ void waybar::Client::handleOutput(struct waybar_output &output) {
|
|||||||
static const struct zxdg_output_v1_listener xdgOutputListener = {
|
static const struct zxdg_output_v1_listener xdgOutputListener = {
|
||||||
.logical_position = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {},
|
.logical_position = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {},
|
||||||
.logical_size = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {},
|
.logical_size = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {},
|
||||||
.done = [](void *, struct zxdg_output_v1 *) {},
|
.done = &handleOutputDone,
|
||||||
.name = &handleOutputName,
|
.name = &handleOutputName,
|
||||||
.description = [](void *, struct zxdg_output_v1 *, const char *) {},
|
.description = &handleOutputDescription,
|
||||||
};
|
};
|
||||||
// owned by output->monitor; no need to destroy
|
// owned by output->monitor; no need to destroy
|
||||||
auto wl_output = gdk_wayland_monitor_get_wl_output(output.monitor->gobj());
|
auto wl_output = gdk_wayland_monitor_get_wl_output(output.monitor->gobj());
|
||||||
@ -64,21 +71,26 @@ void waybar::Client::handleOutput(struct waybar_output &output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::Client::isValidOutput(const Json::Value &config, struct waybar_output &output) {
|
bool waybar::Client::isValidOutput(const Json::Value &config, struct waybar_output &output) {
|
||||||
bool found = true;
|
|
||||||
if (config["output"].isArray()) {
|
if (config["output"].isArray()) {
|
||||||
bool in_array = false;
|
|
||||||
for (auto const &output_conf : config["output"]) {
|
for (auto const &output_conf : config["output"]) {
|
||||||
if (output_conf.isString() && output_conf.asString() == output.name) {
|
if (output_conf.isString() &&
|
||||||
in_array = true;
|
(output_conf.asString() == output.name || output_conf.asString() == output.identifier)) {
|
||||||
break;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
found = in_array;
|
return false;
|
||||||
|
} else if (config["output"].isString()) {
|
||||||
|
auto config_output = config["output"].asString();
|
||||||
|
if (!config_output.empty()) {
|
||||||
|
if (config_output.substr(0, 1) == "!") {
|
||||||
|
return config_output.substr(1) != output.name &&
|
||||||
|
config_output.substr(1) != output.identifier;
|
||||||
}
|
}
|
||||||
if (config["output"].isString() && config["output"].asString() != output.name) {
|
return config_output == output.name || config_output == output.identifier;
|
||||||
found = false;
|
|
||||||
}
|
}
|
||||||
return found;
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct waybar::waybar_output &waybar::Client::getOutput(void *addr) {
|
struct waybar::waybar_output &waybar::Client::getOutput(void *addr) {
|
||||||
@ -104,28 +116,56 @@ std::vector<Json::Value> waybar::Client::getOutputConfigs(struct waybar_output &
|
|||||||
return configs;
|
return configs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_output*/) {
|
||||||
|
auto client = waybar::Client::inst();
|
||||||
|
try {
|
||||||
|
auto &output = client->getOutput(data);
|
||||||
|
/**
|
||||||
|
* Multiple .done events may arrive in batch. In this case libwayland would queue
|
||||||
|
* xdg_output.destroy and dispatch all pending events, triggering this callback several times
|
||||||
|
* for the same output. .done events can also arrive after that for a scale or position changes.
|
||||||
|
* We wouldn't want to draw a duplicate bar for each such event either.
|
||||||
|
*
|
||||||
|
* All the properties we care about are immutable so it's safe to delete the xdg_output object
|
||||||
|
* on the first event and use the ptr value to check that the callback was already invoked.
|
||||||
|
*/
|
||||||
|
if (output.xdg_output) {
|
||||||
|
output.xdg_output.reset();
|
||||||
|
spdlog::debug("Output detection done: {} ({})", output.name, output.identifier);
|
||||||
|
|
||||||
|
auto configs = client->getOutputConfigs(output);
|
||||||
|
if (!configs.empty()) {
|
||||||
|
for (const auto &config : configs) {
|
||||||
|
client->bars.emplace_back(std::make_unique<Bar>(&output, config));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void waybar::Client::handleOutputName(void * data, struct zxdg_output_v1 * /*xdg_output*/,
|
void waybar::Client::handleOutputName(void * data, struct zxdg_output_v1 * /*xdg_output*/,
|
||||||
const char *name) {
|
const char *name) {
|
||||||
auto client = waybar::Client::inst();
|
auto client = waybar::Client::inst();
|
||||||
try {
|
try {
|
||||||
auto &output = client->getOutput(data);
|
auto &output = client->getOutput(data);
|
||||||
output.name = name;
|
output.name = name;
|
||||||
spdlog::debug("Output detected: {} ({} {})",
|
} catch (const std::exception &e) {
|
||||||
name,
|
std::cerr << e.what() << std::endl;
|
||||||
output.monitor->get_manufacturer(),
|
|
||||||
output.monitor->get_model());
|
|
||||||
auto configs = client->getOutputConfigs(output);
|
|
||||||
if (configs.empty()) {
|
|
||||||
output.xdg_output.reset();
|
|
||||||
} else {
|
|
||||||
wl_display_roundtrip(client->wl_display);
|
|
||||||
for (const auto &config : configs) {
|
|
||||||
client->bars.emplace_back(std::make_unique<Bar>(&output, config));
|
|
||||||
Glib::RefPtr<Gdk::Screen> screen = client->bars.back()->window.get_screen();
|
|
||||||
client->style_context_->add_provider_for_screen(
|
|
||||||
screen, client->css_provider_, GTK_STYLE_PROVIDER_PRIORITY_USER);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * /*xdg_output*/,
|
||||||
|
const char *description) {
|
||||||
|
auto client = waybar::Client::inst();
|
||||||
|
try {
|
||||||
|
auto & output = client->getOutput(data);
|
||||||
|
const char *open_paren = strrchr(description, '(');
|
||||||
|
|
||||||
|
// Description format: "identifier (name)"
|
||||||
|
size_t identifier_length = open_paren - description;
|
||||||
|
output.identifier = std::string(description, identifier_length - 1);
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
@ -139,10 +179,21 @@ void waybar::Client::handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor) {
|
|||||||
|
|
||||||
void waybar::Client::handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor) {
|
void waybar::Client::handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor) {
|
||||||
spdlog::debug("Output removed: {} {}", monitor->get_manufacturer(), monitor->get_model());
|
spdlog::debug("Output removed: {} {}", monitor->get_manufacturer(), monitor->get_model());
|
||||||
|
/* This event can be triggered from wl_display_roundtrip called by GTK or our code.
|
||||||
|
* Defer destruction of bars for the output to the next iteration of the event loop to avoid
|
||||||
|
* deleting objects referenced by currently executed code.
|
||||||
|
*/
|
||||||
|
Glib::signal_idle().connect_once(
|
||||||
|
sigc::bind(sigc::mem_fun(*this, &Client::handleDeferredMonitorRemoval), monitor),
|
||||||
|
Glib::PRIORITY_HIGH_IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::Client::handleDeferredMonitorRemoval(Glib::RefPtr<Gdk::Monitor> monitor) {
|
||||||
for (auto it = bars.begin(); it != bars.end();) {
|
for (auto it = bars.begin(); it != bars.end();) {
|
||||||
if ((*it)->output->monitor == monitor) {
|
if ((*it)->output->monitor == monitor) {
|
||||||
auto output_name = (*it)->output->name;
|
auto output_name = (*it)->output->name;
|
||||||
(*it)->window.close();
|
(*it)->window.hide();
|
||||||
|
gtk_app->remove_window((*it)->window);
|
||||||
it = bars.erase(it);
|
it = bars.erase(it);
|
||||||
spdlog::info("Bar removed from output: {}", output_name);
|
spdlog::info("Bar removed from output: {}", output_name);
|
||||||
} else {
|
} else {
|
||||||
@ -159,6 +210,7 @@ std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
|
|||||||
"$HOME/.config/waybar/config",
|
"$HOME/.config/waybar/config",
|
||||||
"$HOME/waybar/config",
|
"$HOME/waybar/config",
|
||||||
"/etc/xdg/waybar/config",
|
"/etc/xdg/waybar/config",
|
||||||
|
SYSCONFDIR "/xdg/waybar/config",
|
||||||
"./resources/config",
|
"./resources/config",
|
||||||
})
|
})
|
||||||
: config;
|
: config;
|
||||||
@ -167,6 +219,7 @@ std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
|
|||||||
"$HOME/.config/waybar/style.css",
|
"$HOME/.config/waybar/style.css",
|
||||||
"$HOME/waybar/style.css",
|
"$HOME/waybar/style.css",
|
||||||
"/etc/xdg/waybar/style.css",
|
"/etc/xdg/waybar/style.css",
|
||||||
|
SYSCONFDIR "/xdg/waybar/style.css",
|
||||||
"./resources/style.css",
|
"./resources/style.css",
|
||||||
})
|
})
|
||||||
: style;
|
: style;
|
||||||
@ -195,6 +248,9 @@ auto waybar::Client::setupCss(const std::string &css_file) -> void {
|
|||||||
if (!css_provider_->load_from_path(css_file)) {
|
if (!css_provider_->load_from_path(css_file)) {
|
||||||
throw std::runtime_error("Can't open style file");
|
throw std::runtime_error("Can't open style file");
|
||||||
}
|
}
|
||||||
|
// there's always only one screen
|
||||||
|
style_context_->add_provider_for_screen(
|
||||||
|
Gdk::Screen::get_default(), css_provider_, GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::Client::bindInterfaces() {
|
void waybar::Client::bindInterfaces() {
|
||||||
@ -249,7 +305,8 @@ int waybar::Client::main(int argc, char *argv[]) {
|
|||||||
if (!log_level.empty()) {
|
if (!log_level.empty()) {
|
||||||
spdlog::set_level(spdlog::level::from_str(log_level));
|
spdlog::set_level(spdlog::level::from_str(log_level));
|
||||||
}
|
}
|
||||||
gtk_app = Gtk::Application::create(argc, argv, "fr.arouillard.waybar");
|
gtk_app = Gtk::Application::create(
|
||||||
|
argc, argv, "fr.arouillard.waybar", Gio::APPLICATION_HANDLES_COMMAND_LINE);
|
||||||
gdk_display = Gdk::Display::get_default();
|
gdk_display = Gdk::Display::get_default();
|
||||||
if (!gdk_display) {
|
if (!gdk_display) {
|
||||||
throw std::runtime_error("Can't find display");
|
throw std::runtime_error("Can't find display");
|
||||||
@ -265,10 +322,9 @@ int waybar::Client::main(int argc, char *argv[]) {
|
|||||||
gtk_app->hold();
|
gtk_app->hold();
|
||||||
gtk_app->run();
|
gtk_app->run();
|
||||||
bars.clear();
|
bars.clear();
|
||||||
zxdg_output_manager_v1_destroy(xdg_output_manager);
|
|
||||||
zwlr_layer_shell_v1_destroy(layer_shell);
|
|
||||||
zwp_idle_inhibit_manager_v1_destroy(idle_inhibit_manager);
|
|
||||||
wl_registry_destroy(registry);
|
|
||||||
wl_display_disconnect(wl_display);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::Client::reset() {
|
||||||
|
gtk_app->quit();
|
||||||
|
}
|
||||||
|
@ -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) : "";
|
||||||
#ifndef NO_FILESYSTEM
|
#if 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,23 +22,40 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
if (ref == "sway/window") {
|
if (ref == "sway/window") {
|
||||||
return new waybar::modules::sway::Window(id, bar_, config_[name]);
|
return new waybar::modules::sway::Window(id, bar_, config_[name]);
|
||||||
}
|
}
|
||||||
|
if (ref == "sway/language") {
|
||||||
|
return new waybar::modules::sway::Language(id, config_[name]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_WLR
|
||||||
|
if (ref == "wlr/taskbar") {
|
||||||
|
return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_RIVER
|
||||||
|
if (ref == "river/tags") {
|
||||||
|
return new waybar::modules::river::Tags(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]);
|
||||||
}
|
}
|
||||||
|
#if defined(HAVE_MEMORY_LINUX) || defined(HAVE_MEMORY_BSD)
|
||||||
if (ref == "memory") {
|
if (ref == "memory") {
|
||||||
return new waybar::modules::Memory(id, config_[name]);
|
return new waybar::modules::Memory(id, config_[name]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD)
|
||||||
if (ref == "cpu") {
|
if (ref == "cpu") {
|
||||||
return new waybar::modules::Cpu(id, config_[name]);
|
return new waybar::modules::Cpu(id, config_[name]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (ref == "clock") {
|
if (ref == "clock") {
|
||||||
return new waybar::modules::Clock(id, config_[name]);
|
return new waybar::modules::Clock(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]);
|
||||||
}
|
}
|
||||||
#if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM)
|
#ifdef HAVE_DBUSMENU
|
||||||
if (ref == "tray") {
|
if (ref == "tray") {
|
||||||
return new waybar::modules::SNI::Tray(id, bar_, config_[name]);
|
return new waybar::modules::SNI::Tray(id, bar_, config_[name]);
|
||||||
}
|
}
|
||||||
@ -62,10 +79,22 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
if (ref == "mpd") {
|
if (ref == "mpd") {
|
||||||
return new waybar::modules::MPD(id, config_[name]);
|
return new waybar::modules::MPD(id, config_[name]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LIBSNDIO
|
||||||
|
if (ref == "sndio") {
|
||||||
|
return new waybar::modules::Sndio(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 defined(__linux__)
|
||||||
|
# ifdef WANT_RFKILL
|
||||||
|
if (ref == "bluetooth") {
|
||||||
|
return new waybar::modules::Bluetooth(id, config_[name]);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
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]);
|
||||||
}
|
}
|
||||||
|
81
src/main.cpp
81
src/main.cpp
@ -1,16 +1,89 @@
|
|||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
#include <list>
|
||||||
|
#include <mutex>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
|
|
||||||
|
std::mutex reap_mtx;
|
||||||
|
std::list<pid_t> reap;
|
||||||
|
volatile bool reload;
|
||||||
|
|
||||||
|
void* signalThread(void* args) {
|
||||||
|
int err, signum;
|
||||||
|
sigset_t mask;
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, SIGCHLD);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
err = sigwait(&mask, &signum);
|
||||||
|
if (err != 0) {
|
||||||
|
spdlog::error("sigwait failed: {}", strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (signum) {
|
||||||
|
case SIGCHLD:
|
||||||
|
spdlog::debug("Received SIGCHLD in signalThread");
|
||||||
|
if (!reap.empty()) {
|
||||||
|
reap_mtx.lock();
|
||||||
|
for (auto it = reap.begin(); it != reap.end(); ++it) {
|
||||||
|
if (waitpid(*it, nullptr, WNOHANG) == *it) {
|
||||||
|
spdlog::debug("Reaped child with PID: {}", *it);
|
||||||
|
it = reap.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reap_mtx.unlock();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
spdlog::debug("Received signal with number {}, but not handling",
|
||||||
|
signum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startSignalThread(void) {
|
||||||
|
int err;
|
||||||
|
sigset_t mask;
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, SIGCHLD);
|
||||||
|
|
||||||
|
// Block SIGCHLD so it can be handled by the signal thread
|
||||||
|
// Any threads created by this one (the main thread) should not
|
||||||
|
// modify their signal mask to unblock SIGCHLD
|
||||||
|
err = pthread_sigmask(SIG_BLOCK, &mask, nullptr);
|
||||||
|
if (err != 0) {
|
||||||
|
spdlog::error("pthread_sigmask failed in startSignalThread: {}", strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_t thread_id;
|
||||||
|
err = pthread_create(&thread_id, nullptr, signalThread, nullptr);
|
||||||
|
if (err != 0) {
|
||||||
|
spdlog::error("pthread_create failed in startSignalThread: {}", strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
try {
|
try {
|
||||||
auto client = waybar::Client::inst();
|
auto client = waybar::Client::inst();
|
||||||
|
|
||||||
std::signal(SIGUSR1, [](int /*signal*/) {
|
std::signal(SIGUSR1, [](int /*signal*/) {
|
||||||
for (auto& bar : waybar::Client::inst()->bars) {
|
for (auto& bar : waybar::Client::inst()->bars) {
|
||||||
bar->toggle();
|
bar->toggle();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
std::signal(SIGUSR2, [](int /*signal*/) {
|
||||||
|
spdlog::info("Reloading...");
|
||||||
|
reload = true;
|
||||||
|
waybar::Client::inst()->reset();
|
||||||
|
});
|
||||||
|
|
||||||
for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) {
|
for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) {
|
||||||
std::signal(sig, [](int sig) {
|
std::signal(sig, [](int sig) {
|
||||||
for (auto& bar : waybar::Client::inst()->bars) {
|
for (auto& bar : waybar::Client::inst()->bars) {
|
||||||
@ -18,8 +91,14 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
startSignalThread();
|
||||||
|
|
||||||
|
auto ret = 0;
|
||||||
|
do {
|
||||||
|
reload = false;
|
||||||
|
ret = client->main(argc, argv);
|
||||||
|
} while (reload);
|
||||||
|
|
||||||
auto ret = client->main(argc, argv);
|
|
||||||
delete client;
|
delete client;
|
||||||
return ret;
|
return ret;
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
#include "modules/backlight.hpp"
|
#include "modules/backlight.hpp"
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <libudev.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <libudev.h>
|
|
||||||
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class FileDescriptor {
|
class FileDescriptor {
|
||||||
public:
|
public:
|
||||||
@ -187,6 +185,8 @@ auto waybar::modules::Backlight::update() -> void {
|
|||||||
}
|
}
|
||||||
previous_best_ = best == nullptr ? std::nullopt : std::optional{*best};
|
previous_best_ = best == nullptr ? std::nullopt : std::optional{*best};
|
||||||
previous_format_ = format_;
|
previous_format_ = format_;
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ForwardIt>
|
template <class ForwardIt>
|
||||||
@ -211,7 +211,7 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last,
|
|||||||
check_nn(name);
|
check_nn(name);
|
||||||
|
|
||||||
const char *actual_brightness_attr =
|
const char *actual_brightness_attr =
|
||||||
strcmp(name, "amdgpu_bl0") == 0 ? "brightness" : "actual_brightness";
|
strncmp(name, "amdgpu_bl", 9) == 0 ? "brightness" : "actual_brightness";
|
||||||
|
|
||||||
const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr);
|
const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr);
|
||||||
check_nn(actual);
|
check_nn(actual);
|
||||||
|
@ -1,48 +1,85 @@
|
|||||||
#include "modules/battery.hpp"
|
#include "modules/battery.hpp"
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
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) {
|
||||||
getBatteries();
|
battery_watch_fd_ = inotify_init1(IN_CLOEXEC);
|
||||||
fd_ = inotify_init1(IN_CLOEXEC);
|
if (battery_watch_fd_ == -1) {
|
||||||
if (fd_ == -1) {
|
|
||||||
throw std::runtime_error("Unable to listen batteries.");
|
throw std::runtime_error("Unable to listen batteries.");
|
||||||
}
|
}
|
||||||
for (auto const& bat : batteries_) {
|
|
||||||
auto wd = inotify_add_watch(fd_, (bat / "uevent").c_str(), IN_ACCESS);
|
global_watch_fd_ = inotify_init1(IN_CLOEXEC);
|
||||||
if (wd != -1) {
|
if (global_watch_fd_ == -1) {
|
||||||
wds_.push_back(wd);
|
throw std::runtime_error("Unable to listen batteries.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Watch the directory for any added or removed batteries
|
||||||
|
global_watch = inotify_add_watch(global_watch_fd_, data_dir_.c_str(), IN_CREATE | IN_DELETE);
|
||||||
|
if (global_watch < 0) {
|
||||||
|
throw std::runtime_error("Could not watch for battery plug/unplug");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshBatteries();
|
||||||
worker();
|
worker();
|
||||||
}
|
}
|
||||||
|
|
||||||
waybar::modules::Battery::~Battery() {
|
waybar::modules::Battery::~Battery() {
|
||||||
for (auto wd : wds_) {
|
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
||||||
inotify_rm_watch(fd_, wd);
|
|
||||||
|
if (global_watch >= 0) {
|
||||||
|
inotify_rm_watch(global_watch_fd_, global_watch);
|
||||||
}
|
}
|
||||||
close(fd_);
|
close(global_watch_fd_);
|
||||||
|
|
||||||
|
for (auto it = batteries_.cbegin(); it != batteries_.cend(); it++) {
|
||||||
|
auto watch_id = (*it).second;
|
||||||
|
if (watch_id >= 0) {
|
||||||
|
inotify_rm_watch(battery_watch_fd_, watch_id);
|
||||||
|
}
|
||||||
|
batteries_.erase(it);
|
||||||
|
}
|
||||||
|
close(battery_watch_fd_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Battery::worker() {
|
void waybar::modules::Battery::worker() {
|
||||||
thread_timer_ = [this] {
|
thread_timer_ = [this] {
|
||||||
|
// Make sure we eventually update the list of batteries even if we miss an
|
||||||
|
// inotify event for some reason
|
||||||
|
refreshBatteries();
|
||||||
dp.emit();
|
dp.emit();
|
||||||
thread_timer_.sleep_for(interval_);
|
thread_timer_.sleep_for(interval_);
|
||||||
};
|
};
|
||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
struct inotify_event event = {0};
|
struct inotify_event event = {0};
|
||||||
int nbytes = read(fd_, &event, sizeof(event));
|
int nbytes = read(battery_watch_fd_, &event, sizeof(event));
|
||||||
if (nbytes != sizeof(event) || event.mask & IN_IGNORED) {
|
if (nbytes != sizeof(event) || event.mask & IN_IGNORED) {
|
||||||
thread_.stop();
|
thread_.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: don't stop timer for now since there is some bugs :?
|
dp.emit();
|
||||||
// thread_timer_.stop();
|
};
|
||||||
|
thread_battery_update_ = [this] {
|
||||||
|
struct inotify_event event = {0};
|
||||||
|
int nbytes = read(global_watch_fd_, &event, sizeof(event));
|
||||||
|
if (nbytes != sizeof(event) || event.mask & IN_IGNORED) {
|
||||||
|
thread_.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
refreshBatteries();
|
||||||
dp.emit();
|
dp.emit();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Battery::getBatteries() {
|
void waybar::modules::Battery::refreshBatteries() {
|
||||||
|
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
||||||
|
|
||||||
|
// Mark existing list of batteries as not necessarily found
|
||||||
|
std::map<fs::path, bool> check_map;
|
||||||
|
for (auto const& bat : batteries_) {
|
||||||
|
check_map[bat.first] = false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (auto& node : fs::directory_iterator(data_dir_)) {
|
for (auto& node : fs::directory_iterator(data_dir_)) {
|
||||||
if (!fs::is_directory(node)) {
|
if (!fs::is_directory(node)) {
|
||||||
@ -52,8 +89,23 @@ void waybar::modules::Battery::getBatteries() {
|
|||||||
auto bat_defined = config_["bat"].isString();
|
auto bat_defined = config_["bat"].isString();
|
||||||
if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) &&
|
if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) &&
|
||||||
fs::exists(node.path() / "capacity") && fs::exists(node.path() / "uevent") &&
|
fs::exists(node.path() / "capacity") && fs::exists(node.path() / "uevent") &&
|
||||||
fs::exists(node.path() / "status")) {
|
fs::exists(node.path() / "status") && fs::exists(node.path() / "type")) {
|
||||||
batteries_.push_back(node.path());
|
std::string type;
|
||||||
|
std::ifstream(node.path() / "type") >> type;
|
||||||
|
|
||||||
|
if (!type.compare("Battery")){
|
||||||
|
check_map[node.path()] = true;
|
||||||
|
auto search = batteries_.find(node.path());
|
||||||
|
if (search == batteries_.end()) {
|
||||||
|
// We've found a new battery save it and start listening for events
|
||||||
|
auto event_path = (node.path() / "uevent");
|
||||||
|
auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS);
|
||||||
|
if (wd < 0) {
|
||||||
|
throw std::runtime_error("Could not watch events for " + node.path().string());
|
||||||
|
}
|
||||||
|
batteries_[node.path()] = wd;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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) &&
|
||||||
@ -70,22 +122,45 @@ void waybar::modules::Battery::getBatteries() {
|
|||||||
}
|
}
|
||||||
throw std::runtime_error("No batteries.");
|
throw std::runtime_error("No batteries.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove any batteries that are no longer present and unwatch them
|
||||||
|
for (auto const& check : check_map) {
|
||||||
|
if (!check.second) {
|
||||||
|
auto watch_id = batteries_[check.first];
|
||||||
|
if (watch_id >= 0) {
|
||||||
|
inotify_rm_watch(battery_watch_fd_, watch_id);
|
||||||
|
}
|
||||||
|
batteries_.erase(check.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::tuple<uint8_t, float, std::string> waybar::modules::Battery::getInfos() const {
|
// Unknown > Full > Not charging > Discharging > Charging
|
||||||
|
static bool status_gt(const std::string& a, const std::string& b) {
|
||||||
|
if (a == b) return false;
|
||||||
|
else if (a == "Unknown") return true;
|
||||||
|
else if (a == "Full" && b != "Unknown") return true;
|
||||||
|
else if (a == "Not charging" && b != "Unknown" && b != "Full") return true;
|
||||||
|
else if (a == "Discharging" && b != "Unknown" && b != "Full" && b != "Not charging") return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::getInfos() {
|
||||||
|
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
uint16_t total = 0;
|
|
||||||
uint32_t total_power = 0; // μW
|
uint32_t total_power = 0; // μW
|
||||||
uint32_t total_energy = 0; // μWh
|
uint32_t total_energy = 0; // μWh
|
||||||
uint32_t total_energy_full = 0;
|
uint32_t total_energy_full = 0;
|
||||||
|
uint32_t total_energy_full_design = 0;
|
||||||
std::string status = "Unknown";
|
std::string status = "Unknown";
|
||||||
for (auto const& bat : batteries_) {
|
for (auto const& item : batteries_) {
|
||||||
uint16_t capacity;
|
auto bat = item.first;
|
||||||
uint32_t power_now;
|
uint32_t power_now;
|
||||||
uint32_t energy_full;
|
uint32_t energy_full;
|
||||||
uint32_t energy_now;
|
uint32_t energy_now;
|
||||||
|
uint32_t energy_full_design;
|
||||||
std::string _status;
|
std::string _status;
|
||||||
std::ifstream(bat / "capacity") >> capacity;
|
|
||||||
std::ifstream(bat / "status") >> _status;
|
std::ifstream(bat / "status") >> _status;
|
||||||
auto rate_path = fs::exists(bat / "current_now") ? "current_now" : "power_now";
|
auto rate_path = fs::exists(bat / "current_now") ? "current_now" : "power_now";
|
||||||
std::ifstream(bat / rate_path) >> power_now;
|
std::ifstream(bat / rate_path) >> power_now;
|
||||||
@ -93,13 +168,17 @@ const std::tuple<uint8_t, float, std::string> waybar::modules::Battery::getInfos
|
|||||||
std::ifstream(bat / now_path) >> energy_now;
|
std::ifstream(bat / now_path) >> energy_now;
|
||||||
auto full_path = fs::exists(bat / "charge_full") ? "charge_full" : "energy_full";
|
auto full_path = fs::exists(bat / "charge_full") ? "charge_full" : "energy_full";
|
||||||
std::ifstream(bat / full_path) >> energy_full;
|
std::ifstream(bat / full_path) >> energy_full;
|
||||||
if (_status != "Unknown") {
|
auto full_design_path = fs::exists(bat / "charge_full_design") ? "charge_full_design" : "energy_full_design";
|
||||||
|
std::ifstream(bat / full_design_path) >> energy_full_design;
|
||||||
|
|
||||||
|
// Show the "smallest" status among all batteries
|
||||||
|
if (status_gt(status, _status)) {
|
||||||
status = _status;
|
status = _status;
|
||||||
}
|
}
|
||||||
total += capacity;
|
|
||||||
total_power += power_now;
|
total_power += power_now;
|
||||||
total_energy += energy_now;
|
total_energy += energy_now;
|
||||||
total_energy_full += energy_full;
|
total_energy_full += energy_full;
|
||||||
|
total_energy_full_design += energy_full_design;
|
||||||
}
|
}
|
||||||
if (!adapter_.empty() && status == "Discharging") {
|
if (!adapter_.empty() && status == "Discharging") {
|
||||||
bool online;
|
bool online;
|
||||||
@ -113,12 +192,40 @@ const std::tuple<uint8_t, float, std::string> waybar::modules::Battery::getInfos
|
|||||||
time_remaining = (float)total_energy / total_power;
|
time_remaining = (float)total_energy / total_power;
|
||||||
} else if (status == "Charging" && total_power != 0) {
|
} else if (status == "Charging" && total_power != 0) {
|
||||||
time_remaining = -(float)(total_energy_full - total_energy) / total_power;
|
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;
|
||||||
}
|
}
|
||||||
uint16_t capacity = total / batteries_.size();
|
}
|
||||||
return {capacity, time_remaining, status};
|
float capacity = ((float)total_energy * 100.0f / (float) total_energy_full);
|
||||||
|
// Handle design-capacity
|
||||||
|
if (config_["design-capacity"].isBool() ? config_["design-capacity"].asBool() : false) {
|
||||||
|
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) {
|
||||||
|
capacity = 100.f * capacity / full_at;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (capacity > 100.f) {
|
||||||
|
// This can happen when the battery is calibrating and goes above 100%
|
||||||
|
// Handle it gracefully by clamping at 100%
|
||||||
|
capacity = 100.f;
|
||||||
|
}
|
||||||
|
uint8_t cap = round(capacity);
|
||||||
|
if (cap == 100) {
|
||||||
|
// If we've reached 100% just mark as full as some batteries can stay
|
||||||
|
// stuck reporting they're still charging but not yet done
|
||||||
|
status = "Full";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {cap, time_remaining, status, total_power / 1e6};
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
spdlog::error("Battery: {}", e.what());
|
spdlog::error("Battery: {}", e.what());
|
||||||
return {0, 0, "Unknown"};
|
return {0, 0, "Unknown", 0};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +249,10 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai
|
|||||||
uint16_t full_hours = static_cast<uint16_t>(hoursRemaining);
|
uint16_t full_hours = static_cast<uint16_t>(hoursRemaining);
|
||||||
uint16_t minutes = static_cast<uint16_t>(60 * (hoursRemaining - full_hours));
|
uint16_t minutes = static_cast<uint16_t>(60 * (hoursRemaining - full_hours));
|
||||||
auto format = std::string("{H} h {M} min");
|
auto format = std::string("{H} h {M} min");
|
||||||
|
if (full_hours == 0 && minutes == 0) {
|
||||||
|
// Migh as well not show "0h 0min"
|
||||||
|
return "";
|
||||||
|
}
|
||||||
if (config_["format-time"].isString()) {
|
if (config_["format-time"].isString()) {
|
||||||
format = config_["format-time"].asString();
|
format = config_["format-time"].asString();
|
||||||
}
|
}
|
||||||
@ -149,28 +260,41 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Battery::update() -> void {
|
auto waybar::modules::Battery::update() -> void {
|
||||||
auto [capacity, time_remaining, status] = getInfos();
|
auto [capacity, time_remaining, status, power] = getInfos();
|
||||||
if (status == "Unknown") {
|
if (status == "Unknown") {
|
||||||
status = getAdapterStatus(capacity);
|
status = getAdapterStatus(capacity);
|
||||||
}
|
}
|
||||||
if (tooltipEnabled()) {
|
auto status_pretty = status;
|
||||||
std::string tooltip_text;
|
// Transform to lowercase and replace space with dash
|
||||||
if (time_remaining != 0) {
|
|
||||||
std::string time_to = std::string("Time to ") + ((time_remaining > 0) ? "empty" : "full");
|
|
||||||
tooltip_text = time_to + ": " + formatTimeRemaining(time_remaining);
|
|
||||||
} else {
|
|
||||||
tooltip_text = status;
|
|
||||||
}
|
|
||||||
label_.set_tooltip_text(tooltip_text);
|
|
||||||
}
|
|
||||||
// Transform to lowercase
|
|
||||||
std::transform(status.begin(), status.end(), status.begin(), ::tolower);
|
|
||||||
// Replace space with dash
|
|
||||||
std::transform(status.begin(), status.end(), status.begin(), [](char ch) {
|
std::transform(status.begin(), status.end(), status.begin(), [](char ch) {
|
||||||
return ch == ' ' ? '-' : ch;
|
return ch == ' ' ? '-' : std::tolower(ch);
|
||||||
});
|
});
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
auto state = getState(capacity, true);
|
auto state = getState(capacity, true);
|
||||||
|
auto time_remaining_formatted = formatTimeRemaining(time_remaining);
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
std::string tooltip_text_default;
|
||||||
|
std::string tooltip_format = "{timeTo}";
|
||||||
|
if (time_remaining != 0) {
|
||||||
|
std::string time_to = std::string("Time to ") + ((time_remaining > 0) ? "empty" : "full");
|
||||||
|
tooltip_text_default = time_to + ": " + time_remaining_formatted;
|
||||||
|
} else {
|
||||||
|
tooltip_text_default = status_pretty;
|
||||||
|
}
|
||||||
|
if (!state.empty() && config_["tooltip-format-" + status + "-" + state].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format-" + status + "-" + state].asString();
|
||||||
|
} else if (config_["tooltip-format-" + status].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format-" + status].asString();
|
||||||
|
} else if (!state.empty() && config_["tooltip-format-" + state].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format-" + state].asString();
|
||||||
|
} else if (config_["tooltip-format"].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
}
|
||||||
|
label_.set_tooltip_text(fmt::format(tooltip_format,
|
||||||
|
fmt::arg("timeTo", tooltip_text_default),
|
||||||
|
fmt::arg("capacity", capacity),
|
||||||
|
fmt::arg("time", time_remaining_formatted)));
|
||||||
|
}
|
||||||
if (!old_status_.empty()) {
|
if (!old_status_.empty()) {
|
||||||
label_.get_style_context()->remove_class(old_status_);
|
label_.get_style_context()->remove_class(old_status_);
|
||||||
}
|
}
|
||||||
@ -187,9 +311,13 @@ auto waybar::modules::Battery::update() -> void {
|
|||||||
event_box_.hide();
|
event_box_.hide();
|
||||||
} else {
|
} else {
|
||||||
event_box_.show();
|
event_box_.show();
|
||||||
|
auto icons = std::vector<std::string>{status + "-" + state, status, state};
|
||||||
label_.set_markup(fmt::format(format,
|
label_.set_markup(fmt::format(format,
|
||||||
fmt::arg("capacity", capacity),
|
fmt::arg("capacity", capacity),
|
||||||
fmt::arg("icon", getIcon(capacity, state)),
|
fmt::arg("power", power),
|
||||||
fmt::arg("time", formatTimeRemaining(time_remaining))));
|
fmt::arg("icon", getIcon(capacity, icons)),
|
||||||
|
fmt::arg("time", time_remaining_formatted)));
|
||||||
}
|
}
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
25
src/modules/bluetooth.cpp
Normal file
25
src/modules/bluetooth.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "modules/bluetooth.hpp"
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config)
|
||||||
|
: ALabel(config, "bluetooth", id, "{icon}", 10), rfkill_{RFKILL_TYPE_BLUETOOTH} {
|
||||||
|
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Bluetooth::update() -> void {
|
||||||
|
std::string status = rfkill_.getState() ? "disabled" : "enabled";
|
||||||
|
|
||||||
|
label_.set_markup(
|
||||||
|
fmt::format(format_, fmt::arg("status", status), fmt::arg("icon", getIcon(0, status))));
|
||||||
|
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
if (config_["tooltip-format"].isString()) {
|
||||||
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
auto tooltip_text = fmt::format(tooltip_format, status);
|
||||||
|
label_.set_tooltip_text(tooltip_text);
|
||||||
|
} else {
|
||||||
|
label_.set_tooltip_text(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,11 @@
|
|||||||
#include "modules/clock.hpp"
|
#include "modules/clock.hpp"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include "util/ustring_clen.hpp"
|
||||||
#ifdef HAVE_LANGINFO_1STDAY
|
#ifdef HAVE_LANGINFO_1STDAY
|
||||||
#include <langinfo.h>
|
#include <langinfo.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
@ -9,10 +14,9 @@
|
|||||||
using waybar::modules::waybar_time;
|
using waybar::modules::waybar_time;
|
||||||
|
|
||||||
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||||
: ALabel(config, "clock", id, "{:%H:%M}", 60)
|
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), fixed_time_zone_(false) {
|
||||||
, fixed_time_zone_(false)
|
|
||||||
{
|
|
||||||
if (config_["timezone"].isString()) {
|
if (config_["timezone"].isString()) {
|
||||||
|
spdlog::warn("As using a timezone, some format args may be missing as the date library havn't got a release since 2018.");
|
||||||
time_zone_ = date::locate_zone(config_["timezone"].asString());
|
time_zone_ = date::locate_zone(config_["timezone"].asString());
|
||||||
fixed_time_zone_ = true;
|
fixed_time_zone_ = true;
|
||||||
}
|
}
|
||||||
@ -37,12 +41,22 @@ auto waybar::modules::Clock::update() -> void {
|
|||||||
// Time zone can change. Be sure to pick that.
|
// Time zone can change. Be sure to pick that.
|
||||||
time_zone_ = date::current_zone();
|
time_zone_ = date::current_zone();
|
||||||
}
|
}
|
||||||
|
|
||||||
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 text = fmt::format(format_, wtime);
|
std::string text;
|
||||||
|
if (!fixed_time_zone_) {
|
||||||
|
// As date dep is not fully compatible, prefer fmt
|
||||||
|
tzset();
|
||||||
|
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
|
||||||
|
text = fmt::format(format_, localtime);
|
||||||
label_.set_markup(text);
|
label_.set_markup(text);
|
||||||
|
} else {
|
||||||
|
text = fmt::format(format_, wtime);
|
||||||
|
label_.set_markup(text);
|
||||||
|
}
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
if (config_["tooltip-format"].isString()) {
|
if (config_["tooltip-format"].isString()) {
|
||||||
@ -54,6 +68,40 @@ auto waybar::modules::Clock::update() -> void {
|
|||||||
label_.set_tooltip_markup(text);
|
label_.set_tooltip_markup(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) {
|
||||||
|
// defer to user commands if set
|
||||||
|
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
|
||||||
|
return AModule::handleScroll(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = AModule::getScrollDir(e);
|
||||||
|
if (dir != SCROLL_DIR::UP && dir != SCROLL_DIR::DOWN) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!config_["timezones"].isArray() || config_["timezones"].empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto nr_zones = config_["timezones"].size();
|
||||||
|
if (dir == SCROLL_DIR::UP) {
|
||||||
|
size_t new_idx = time_zone_idx_ + 1;
|
||||||
|
time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx;
|
||||||
|
} else {
|
||||||
|
time_zone_idx_ = time_zone_idx_ == 0 ? nr_zones - 1 : time_zone_idx_ - 1;
|
||||||
|
}
|
||||||
|
auto zone_name = config_["timezones"][time_zone_idx_];
|
||||||
|
if (!zone_name.isString() || zone_name.empty()) {
|
||||||
|
fixed_time_zone_ = false;
|
||||||
|
} else {
|
||||||
|
time_zone_ = date::locate_zone(zone_name.asString());
|
||||||
|
fixed_time_zone_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string {
|
auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string {
|
||||||
@ -71,12 +119,12 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str
|
|||||||
weekdays_header(first_dow, os);
|
weekdays_header(first_dow, os);
|
||||||
|
|
||||||
// First week prefixed with spaces if needed.
|
// First week prefixed with spaces if needed.
|
||||||
auto wd = date::weekday(ym/1);
|
auto wd = date::weekday(ym / 1);
|
||||||
auto empty_days = (wd - first_dow).count();
|
auto empty_days = (wd - first_dow).count();
|
||||||
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();
|
auto last_day = (ym / date::literals::last).day();
|
||||||
for (auto d = date::day(1); d <= last_day; ++d, ++wd) {
|
for (auto d = date::day(1); d <= last_day; ++d, ++wd) {
|
||||||
if (wd != first_dow) {
|
if (wd != first_dow) {
|
||||||
os << ' ';
|
os << ' ';
|
||||||
@ -84,7 +132,12 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str
|
|||||||
os << '\n';
|
os << '\n';
|
||||||
}
|
}
|
||||||
if (d == curr_day) {
|
if (d == curr_day) {
|
||||||
|
if (config_["today-format"].isString()) {
|
||||||
|
auto today_format = config_["today-format"].asString();
|
||||||
|
os << fmt::format(today_format, date::format("%e", d));
|
||||||
|
} else {
|
||||||
os << "<b><u>" << date::format("%e", d) << "</u></b>";
|
os << "<b><u>" << date::format("%e", d) << "</u></b>";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
os << date::format("%e", d);
|
os << date::format("%e", d);
|
||||||
}
|
}
|
||||||
@ -96,17 +149,20 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void {
|
auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std::ostream& os)
|
||||||
|
-> void {
|
||||||
auto wd = first_dow;
|
auto wd = first_dow;
|
||||||
do {
|
do {
|
||||||
if (wd != first_dow) os << ' ';
|
if (wd != first_dow) os << ' ';
|
||||||
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 wd_len = wd_ustring.length();
|
auto wd_len = wd_ustring.length();
|
||||||
if (wd_len > 2) {
|
while (clen > 2) {
|
||||||
wd_ustring = wd_ustring.substr(0, 2);
|
wd_ustring = wd_ustring.substr(0, wd_len-1);
|
||||||
wd_len = 2;
|
wd_len--;
|
||||||
|
clen = ustring_clen(wd_ustring);
|
||||||
}
|
}
|
||||||
const std::string pad(2 - wd_len, ' ');
|
const std::string pad(2 - clen, ' ');
|
||||||
os << pad << wd_ustring;
|
os << pad << wd_ustring;
|
||||||
} while (++wd != first_dow);
|
} while (++wd != first_dow);
|
||||||
os << "\n";
|
os << "\n";
|
||||||
@ -123,11 +179,11 @@ using deleting_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
|
|||||||
// Computations done similarly to Linux cal utility.
|
// Computations done similarly to Linux cal utility.
|
||||||
auto waybar::modules::Clock::first_day_of_week() -> date::weekday {
|
auto waybar::modules::Clock::first_day_of_week() -> date::weekday {
|
||||||
#ifdef HAVE_LANGINFO_1STDAY
|
#ifdef HAVE_LANGINFO_1STDAY
|
||||||
deleting_unique_ptr<std::remove_pointer<locale_t>::type, freelocale>
|
deleting_unique_ptr<std::remove_pointer<locale_t>::type, freelocale> posix_locale{
|
||||||
posix_locale{newlocale(LC_ALL, locale_.name().c_str(), nullptr)};
|
newlocale(LC_ALL, locale_.name().c_str(), nullptr)};
|
||||||
if (posix_locale) {
|
if (posix_locale) {
|
||||||
const int i = (std::intptr_t) nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get());
|
const int i = (std::intptr_t)nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get());
|
||||||
auto ymd = date::year(i / 10000)/(i / 100 % 100)/(i % 100);
|
auto ymd = date::year(i / 10000) / (i / 100 % 100) / (i % 100);
|
||||||
auto wd = date::weekday(ymd);
|
auto wd = date::weekday(ymd);
|
||||||
uint8_t j = *nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, posix_locale.get());
|
uint8_t j = *nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, posix_locale.get());
|
||||||
return wd + date::days(j - 1);
|
return wd + date::days(j - 1);
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
#include "modules/cpu.hpp"
|
|
||||||
#include <numeric>
|
|
||||||
|
|
||||||
waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config)
|
|
||||||
: ALabel(config, "cpu", id, "{usage}%", 10) {
|
|
||||||
thread_ = [this] {
|
|
||||||
dp.emit();
|
|
||||||
thread_.sleep_for(interval_);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto waybar::modules::Cpu::update() -> void {
|
|
||||||
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
|
|
||||||
auto cpu_load = getCpuLoad();
|
|
||||||
auto [cpu_usage, tooltip] = getCpuUsage();
|
|
||||||
if (tooltipEnabled()) {
|
|
||||||
label_.set_tooltip_text(tooltip);
|
|
||||||
}
|
|
||||||
label_.set_markup(fmt::format(format_, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage)));
|
|
||||||
getState(cpu_usage);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t waybar::modules::Cpu::getCpuLoad() {
|
|
||||||
struct sysinfo info = {0};
|
|
||||||
if (sysinfo(&info) == 0) {
|
|
||||||
float f_load = 1.F / (1U << SI_LOAD_SHIFT);
|
|
||||||
uint16_t load = info.loads[0] * f_load * 100 / get_nprocs();
|
|
||||||
return load;
|
|
||||||
}
|
|
||||||
throw std::runtime_error("Can't get Cpu load");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<uint16_t, std::string> waybar::modules::Cpu::getCpuUsage() {
|
|
||||||
if (prev_times_.empty()) {
|
|
||||||
prev_times_ = parseCpuinfo();
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
}
|
|
||||||
std::vector<std::tuple<size_t, size_t>> curr_times = parseCpuinfo();
|
|
||||||
std::string tooltip;
|
|
||||||
uint16_t usage = 0;
|
|
||||||
for (size_t i = 0; i < curr_times.size(); ++i) {
|
|
||||||
auto [curr_idle, curr_total] = curr_times[i];
|
|
||||||
auto [prev_idle, prev_total] = prev_times_[i];
|
|
||||||
const float delta_idle = curr_idle - prev_idle;
|
|
||||||
const float delta_total = curr_total - prev_total;
|
|
||||||
uint16_t tmp = 100 * (1 - delta_idle / delta_total);
|
|
||||||
if (i == 0) {
|
|
||||||
usage = tmp;
|
|
||||||
tooltip = fmt::format("Total: {}%", tmp);
|
|
||||||
} else {
|
|
||||||
tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prev_times_ = curr_times;
|
|
||||||
return {usage, tooltip};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo() {
|
|
||||||
std::ifstream info(data_dir_);
|
|
||||||
if (!info.is_open()) {
|
|
||||||
throw std::runtime_error("Can't open " + data_dir_);
|
|
||||||
}
|
|
||||||
std::vector<std::tuple<size_t, size_t>> cpuinfo;
|
|
||||||
std::string line;
|
|
||||||
while (getline(info, line)) {
|
|
||||||
if (line.substr(0, 3).compare("cpu") != 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::stringstream sline(line.substr(5));
|
|
||||||
std::vector<size_t> times;
|
|
||||||
for (size_t time = 0; sline >> time; times.push_back(time))
|
|
||||||
;
|
|
||||||
|
|
||||||
size_t idle_time = 0;
|
|
||||||
size_t total_time = 0;
|
|
||||||
if (times.size() >= 4) {
|
|
||||||
idle_time = times[3];
|
|
||||||
total_time = std::accumulate(times.begin(), times.end(), 0);
|
|
||||||
}
|
|
||||||
cpuinfo.emplace_back(idle_time, total_time);
|
|
||||||
}
|
|
||||||
return cpuinfo;
|
|
||||||
}
|
|
108
src/modules/cpu/bsd.cpp
Normal file
108
src/modules/cpu/bsd.cpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include "modules/cpu.hpp"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <cstdlib> // malloc
|
||||||
|
#include <unistd.h> // sysconf
|
||||||
|
#include <cmath> // NAN
|
||||||
|
|
||||||
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
||||||
|
# include <sys/sched.h>
|
||||||
|
#else
|
||||||
|
# include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__NetBSD__)
|
||||||
|
typedef uint64_t cp_time_t;
|
||||||
|
#else
|
||||||
|
typedef long cp_time_t;
|
||||||
|
#endif
|
||||||
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
||||||
|
typedef uint64_t pcp_time_t;
|
||||||
|
#else
|
||||||
|
typedef long pcp_time_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo() {
|
||||||
|
cp_time_t sum_cp_time[CPUSTATES];
|
||||||
|
size_t sum_sz = sizeof(sum_cp_time);
|
||||||
|
int ncpu = sysconf(_SC_NPROCESSORS_CONF);
|
||||||
|
size_t sz = CPUSTATES * (ncpu + 1) * sizeof(pcp_time_t);
|
||||||
|
pcp_time_t *cp_time = static_cast<pcp_time_t *>(malloc(sz)), *pcp_time = cp_time;
|
||||||
|
#if defined(__NetBSD__)
|
||||||
|
int mib[] = {
|
||||||
|
CTL_KERN,
|
||||||
|
KERN_CP_TIME,
|
||||||
|
};
|
||||||
|
if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), sum_cp_time, &sum_sz, NULL, 0)) {
|
||||||
|
throw std::runtime_error("sysctl kern.cp_time failed");
|
||||||
|
}
|
||||||
|
for (int state = 0; state < CPUSTATES; state++) {
|
||||||
|
cp_time[state] = sum_cp_time[state];
|
||||||
|
}
|
||||||
|
pcp_time += CPUSTATES;
|
||||||
|
if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), pcp_time, &sz, NULL, 0)) {
|
||||||
|
throw std::runtime_error("sysctl kern.cp_time failed");
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
{
|
||||||
|
int mib[] = {
|
||||||
|
CTL_KERN,
|
||||||
|
KERN_CPTIME,
|
||||||
|
};
|
||||||
|
if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), sum_cp_time, &sum_sz, NULL, 0)) {
|
||||||
|
throw std::runtime_error("sysctl kern.cp_time failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int state = 0; state < CPUSTATES; state++) {
|
||||||
|
cp_time[state] = sum_cp_time[state];
|
||||||
|
}
|
||||||
|
pcp_time = cp_time;
|
||||||
|
sz /= ncpu + 1;
|
||||||
|
{
|
||||||
|
int mib[] = {
|
||||||
|
CTL_KERN,
|
||||||
|
KERN_CPTIME2,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
for (int cpu = 0; cpu < ncpu; cpu++) {
|
||||||
|
mib[2] = cpu;
|
||||||
|
pcp_time += CPUSTATES;
|
||||||
|
if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), pcp_time, &sz, NULL, 0)) {
|
||||||
|
throw std::runtime_error("sysctl kern.cp_time2 failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (sysctlbyname("kern.cp_time", sum_cp_time, &sum_sz, NULL, 0)) {
|
||||||
|
throw std::runtime_error("sysctl kern.cp_time failed");
|
||||||
|
}
|
||||||
|
for (int state = 0; state < CPUSTATES; state++) {
|
||||||
|
cp_time[state] = sum_cp_time[state];
|
||||||
|
}
|
||||||
|
pcp_time += CPUSTATES;
|
||||||
|
if (sysctlbyname("kern.cp_times", pcp_time, &sz, NULL, 0)) {
|
||||||
|
throw std::runtime_error("sysctl kern.cp_times failed");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::vector<std::tuple<size_t, size_t>> cpuinfo;
|
||||||
|
for (int cpu = 0; cpu < ncpu + 1; cpu++) {
|
||||||
|
pcp_time_t total = 0, *single_cp_time = &cp_time[cpu * CPUSTATES];
|
||||||
|
for (int state = 0; state < CPUSTATES; state++) {
|
||||||
|
total += single_cp_time[state];
|
||||||
|
}
|
||||||
|
cpuinfo.emplace_back(single_cp_time[CP_IDLE], total);
|
||||||
|
}
|
||||||
|
free(cp_time);
|
||||||
|
return cpuinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<float> waybar::modules::Cpu::parseCpuFrequencies() {
|
||||||
|
static std::vector<float> frequencies;
|
||||||
|
if (frequencies.empty()) {
|
||||||
|
spdlog::warn("cpu/bsd: parseCpuFrequencies is not implemented, expect garbage in {*_frequency}");
|
||||||
|
frequencies.push_back(NAN);
|
||||||
|
}
|
||||||
|
return frequencies;
|
||||||
|
}
|
85
src/modules/cpu/common.cpp
Normal file
85
src/modules/cpu/common.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#include "modules/cpu.hpp"
|
||||||
|
|
||||||
|
waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config)
|
||||||
|
: ALabel(config, "cpu", id, "{usage}%", 10) {
|
||||||
|
thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
thread_.sleep_for(interval_);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Cpu::update() -> void {
|
||||||
|
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
|
||||||
|
auto cpu_load = getCpuLoad();
|
||||||
|
auto [cpu_usage, tooltip] = getCpuUsage();
|
||||||
|
auto [max_frequency, min_frequency, avg_frequency] = getCpuFrequency();
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
label_.set_tooltip_text(tooltip);
|
||||||
|
}
|
||||||
|
auto format = format_;
|
||||||
|
auto state = getState(cpu_usage);
|
||||||
|
if (!state.empty() && config_["format-" + state].isString()) {
|
||||||
|
format = config_["format-" + state].asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format.empty()) {
|
||||||
|
event_box_.hide();
|
||||||
|
} else {
|
||||||
|
event_box_.show();
|
||||||
|
label_.set_markup(fmt::format(format,
|
||||||
|
fmt::arg("load", cpu_load),
|
||||||
|
fmt::arg("usage", cpu_usage),
|
||||||
|
fmt::arg("max_frequency", max_frequency),
|
||||||
|
fmt::arg("min_frequency", min_frequency),
|
||||||
|
fmt::arg("avg_frequency", avg_frequency)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
double waybar::modules::Cpu::getCpuLoad() {
|
||||||
|
double load[1];
|
||||||
|
if (getloadavg(load, 1) != -1) {
|
||||||
|
return load[0];
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Can't get Cpu load");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<uint16_t, std::string> waybar::modules::Cpu::getCpuUsage() {
|
||||||
|
if (prev_times_.empty()) {
|
||||||
|
prev_times_ = parseCpuinfo();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
}
|
||||||
|
std::vector<std::tuple<size_t, size_t>> curr_times = parseCpuinfo();
|
||||||
|
std::string tooltip;
|
||||||
|
uint16_t usage = 0;
|
||||||
|
for (size_t i = 0; i < curr_times.size(); ++i) {
|
||||||
|
auto [curr_idle, curr_total] = curr_times[i];
|
||||||
|
auto [prev_idle, prev_total] = prev_times_[i];
|
||||||
|
const float delta_idle = curr_idle - prev_idle;
|
||||||
|
const float delta_total = curr_total - prev_total;
|
||||||
|
uint16_t tmp = 100 * (1 - delta_idle / delta_total);
|
||||||
|
if (i == 0) {
|
||||||
|
usage = tmp;
|
||||||
|
tooltip = fmt::format("Total: {}%", tmp);
|
||||||
|
} else {
|
||||||
|
tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_times_ = curr_times;
|
||||||
|
return {usage, tooltip};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<float, float, float> waybar::modules::Cpu::getCpuFrequency() {
|
||||||
|
std::vector<float> frequencies = parseCpuFrequencies();
|
||||||
|
auto [min, max] = std::minmax_element(std::begin(frequencies), std::end(frequencies));
|
||||||
|
float avg_frequency = std::accumulate(std::begin(frequencies), std::end(frequencies), 0.0) / frequencies.size();
|
||||||
|
|
||||||
|
// Round frequencies with double decimal precision to get GHz
|
||||||
|
float max_frequency = std::ceil(*max / 10.0) / 100.0;
|
||||||
|
float min_frequency = std::ceil(*min / 10.0) / 100.0;
|
||||||
|
avg_frequency = std::ceil(avg_frequency / 10.0) / 100.0;
|
||||||
|
|
||||||
|
return { max_frequency, min_frequency, avg_frequency };
|
||||||
|
}
|
50
src/modules/cpu/linux.cpp
Normal file
50
src/modules/cpu/linux.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include "modules/cpu.hpp"
|
||||||
|
|
||||||
|
std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo() {
|
||||||
|
const std::string data_dir_ = "/proc/stat";
|
||||||
|
std::ifstream info(data_dir_);
|
||||||
|
if (!info.is_open()) {
|
||||||
|
throw std::runtime_error("Can't open " + data_dir_);
|
||||||
|
}
|
||||||
|
std::vector<std::tuple<size_t, size_t>> cpuinfo;
|
||||||
|
std::string line;
|
||||||
|
while (getline(info, line)) {
|
||||||
|
if (line.substr(0, 3).compare("cpu") != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::stringstream sline(line.substr(5));
|
||||||
|
std::vector<size_t> times;
|
||||||
|
for (size_t time = 0; sline >> time; times.push_back(time))
|
||||||
|
;
|
||||||
|
|
||||||
|
size_t idle_time = 0;
|
||||||
|
size_t total_time = 0;
|
||||||
|
if (times.size() >= 4) {
|
||||||
|
idle_time = times[3];
|
||||||
|
total_time = std::accumulate(times.begin(), times.end(), 0);
|
||||||
|
}
|
||||||
|
cpuinfo.emplace_back(idle_time, total_time);
|
||||||
|
}
|
||||||
|
return cpuinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<float> waybar::modules::Cpu::parseCpuFrequencies() {
|
||||||
|
const std::string file_path_ = "/proc/cpuinfo";
|
||||||
|
std::ifstream info(file_path_);
|
||||||
|
if (!info.is_open()) {
|
||||||
|
throw std::runtime_error("Can't open " + file_path_);
|
||||||
|
}
|
||||||
|
std::vector<float> frequencies;
|
||||||
|
std::string line;
|
||||||
|
while (getline(info, line)) {
|
||||||
|
if (line.substr(0, 7).compare("cpu MHz") != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string frequency_str = line.substr(line.find(":") + 2);
|
||||||
|
float frequency = std::strtol(frequency_str.c_str(), nullptr, 10);
|
||||||
|
frequencies.push_back(frequency);
|
||||||
|
}
|
||||||
|
info.close();
|
||||||
|
return frequencies;
|
||||||
|
}
|
@ -1,22 +1,21 @@
|
|||||||
#include "modules/custom.hpp"
|
#include "modules/custom.hpp"
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
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), fp_(nullptr), pid_(-1) {
|
||||||
if (config_["exec"].isString()) {
|
dp.emit();
|
||||||
if (interval_.count() > 0) {
|
if (interval_.count() > 0) {
|
||||||
delayWorker();
|
delayWorker();
|
||||||
} else {
|
} else if (config_["exec"].isString()) {
|
||||||
continuousWorker();
|
continuousWorker();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
dp.emit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
waybar::modules::Custom::~Custom() {
|
waybar::modules::Custom::~Custom() {
|
||||||
if (pid_ != -1) {
|
if (pid_ != -1) {
|
||||||
kill(-pid_, 9);
|
killpg(pid_, SIGTERM);
|
||||||
pid_ = -1;
|
pid_ = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,14 +24,16 @@ void waybar::modules::Custom::delayWorker() {
|
|||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
bool can_update = true;
|
bool can_update = true;
|
||||||
if (config_["exec-if"].isString()) {
|
if (config_["exec-if"].isString()) {
|
||||||
auto res = util::command::exec(config_["exec-if"].asString());
|
output_ = util::command::execNoRead(config_["exec-if"].asString());
|
||||||
if (res.exit_code != 0) {
|
if (output_.exit_code != 0) {
|
||||||
can_update = false;
|
can_update = false;
|
||||||
event_box_.hide();
|
dp.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (can_update) {
|
if (can_update) {
|
||||||
|
if (config_["exec"].isString()) {
|
||||||
output_ = util::command::exec(config_["exec"].asString());
|
output_ = util::command::exec(config_["exec"].asString());
|
||||||
|
}
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
thread_.sleep_for(interval_);
|
thread_.sleep_for(interval_);
|
||||||
@ -46,7 +47,7 @@ void waybar::modules::Custom::continuousWorker() {
|
|||||||
if (!fp_) {
|
if (!fp_) {
|
||||||
throw std::runtime_error("Unable to open " + cmd);
|
throw std::runtime_error("Unable to open " + cmd);
|
||||||
}
|
}
|
||||||
thread_ = [&] {
|
thread_ = [this, cmd] {
|
||||||
char* buff = nullptr;
|
char* buff = nullptr;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
if (getline(&buff, &len, fp_) == -1) {
|
if (getline(&buff, &len, fp_) == -1) {
|
||||||
@ -55,14 +56,23 @@ void waybar::modules::Custom::continuousWorker() {
|
|||||||
exit_code = WEXITSTATUS(util::command::close(fp_, pid_));
|
exit_code = WEXITSTATUS(util::command::close(fp_, pid_));
|
||||||
fp_ = nullptr;
|
fp_ = nullptr;
|
||||||
}
|
}
|
||||||
thread_.stop();
|
|
||||||
if (exit_code != 0) {
|
if (exit_code != 0) {
|
||||||
output_ = {exit_code, ""};
|
output_ = {exit_code, ""};
|
||||||
dp.emit();
|
dp.emit();
|
||||||
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
|
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
|
||||||
}
|
}
|
||||||
|
if (config_["restart-interval"].isUInt()) {
|
||||||
|
pid_ = -1;
|
||||||
|
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
|
||||||
|
fp_ = util::command::open(cmd, pid_);
|
||||||
|
if (!fp_) {
|
||||||
|
throw std::runtime_error("Unable to open " + cmd);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thread_.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
std::string output = buff;
|
std::string output = buff;
|
||||||
|
|
||||||
// Remove last newline
|
// Remove last newline
|
||||||
@ -71,6 +81,7 @@ void waybar::modules::Custom::continuousWorker() {
|
|||||||
}
|
}
|
||||||
output_ = {0, output};
|
output_ = {0, output};
|
||||||
dp.emit();
|
dp.emit();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,21 +91,28 @@ void waybar::modules::Custom::refresh(int sig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::modules::Custom::handleEvent() {
|
||||||
|
if (!config_["exec-on-event"].isBool() || config_["exec-on-event"].asBool()) {
|
||||||
|
thread_.wake_up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) {
|
bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) {
|
||||||
auto ret = ALabel::handleScroll(e);
|
auto ret = ALabel::handleScroll(e);
|
||||||
thread_.wake_up();
|
handleEvent();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
|
bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
|
||||||
auto ret = ALabel::handleToggle(e);
|
auto ret = ALabel::handleToggle(e);
|
||||||
thread_.wake_up();
|
handleEvent();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Custom::update() -> void {
|
auto waybar::modules::Custom::update() -> void {
|
||||||
// Hide label if output is empty
|
// Hide label if output is empty
|
||||||
if (config_["exec"].isString() && (output_.out.empty() || output_.exit_code != 0)) {
|
if ((config_["exec"].isString() || config_["exec-if"].isString()) &&
|
||||||
|
(output_.out.empty() || output_.exit_code != 0)) {
|
||||||
event_box_.hide();
|
event_box_.hide();
|
||||||
} else {
|
} else {
|
||||||
if (config_["return-type"].asString() == "json") {
|
if (config_["return-type"].asString() == "json") {
|
||||||
@ -113,9 +131,13 @@ auto waybar::modules::Custom::update() -> void {
|
|||||||
label_.set_markup(str);
|
label_.set_markup(str);
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
if (text_ == tooltip_) {
|
if (text_ == tooltip_) {
|
||||||
label_.set_tooltip_text(str);
|
if (label_.get_tooltip_markup() != str) {
|
||||||
|
label_.set_tooltip_markup(str);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
label_.set_tooltip_text(tooltip_);
|
if (label_.get_tooltip_markup() != tooltip_) {
|
||||||
|
label_.set_tooltip_markup(tooltip_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto classes = label_.get_style_context()->list_classes();
|
auto classes = label_.get_style_context()->list_classes();
|
||||||
@ -128,6 +150,8 @@ auto waybar::modules::Custom::update() -> void {
|
|||||||
event_box_.show();
|
event_box_.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Custom::parseOutputRaw() {
|
void waybar::modules::Custom::parseOutputRaw() {
|
||||||
|
@ -44,19 +44,32 @@ auto waybar::modules::Disk::update() -> void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto free = pow_format(stats.f_bavail * stats.f_bsize, "B", true);
|
auto free = pow_format(stats.f_bavail * stats.f_frsize, "B", true);
|
||||||
auto used = pow_format((stats.f_blocks - stats.f_bavail) * stats.f_bsize, "B", true);
|
auto used = pow_format((stats.f_blocks - stats.f_bavail) * stats.f_frsize, "B", true);
|
||||||
auto total = pow_format(stats.f_blocks * stats.f_bsize, "B", true);
|
auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true);
|
||||||
|
auto percentage_used = (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks;
|
||||||
|
|
||||||
label_.set_markup(fmt::format(format_
|
auto format = format_;
|
||||||
|
auto state = getState(percentage_used);
|
||||||
|
if (!state.empty() && config_["format-" + state].isString()) {
|
||||||
|
format = config_["format-" + state].asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format.empty()) {
|
||||||
|
event_box_.hide();
|
||||||
|
} else {
|
||||||
|
event_box_.show();
|
||||||
|
label_.set_markup(fmt::format(format
|
||||||
, stats.f_bavail * 100 / stats.f_blocks
|
, stats.f_bavail * 100 / stats.f_blocks
|
||||||
, fmt::arg("free", free)
|
, fmt::arg("free", free)
|
||||||
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
|
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
|
||||||
, fmt::arg("used", used)
|
, fmt::arg("used", used)
|
||||||
, fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks)
|
, fmt::arg("percentage_used", percentage_used)
|
||||||
, fmt::arg("total", total)
|
, fmt::arg("total", total)
|
||||||
, fmt::arg("path", path_)
|
, fmt::arg("path", path_)
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)";
|
std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)";
|
||||||
if (config_["tooltip-format"].isString()) {
|
if (config_["tooltip-format"].isString()) {
|
||||||
@ -67,10 +80,11 @@ auto waybar::modules::Disk::update() -> void {
|
|||||||
, fmt::arg("free", free)
|
, fmt::arg("free", free)
|
||||||
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
|
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
|
||||||
, fmt::arg("used", used)
|
, fmt::arg("used", used)
|
||||||
, fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks)
|
, fmt::arg("percentage_used", percentage_used)
|
||||||
, fmt::arg("total", total)
|
, fmt::arg("total", total)
|
||||||
, fmt::arg("path", path_)
|
, fmt::arg("path", path_)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
event_box_.show();
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,28 @@
|
|||||||
#include "modules/idle_inhibitor.hpp"
|
#include "modules/idle_inhibitor.hpp"
|
||||||
|
|
||||||
|
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
#include "util/command.hpp"
|
#include "util/command.hpp"
|
||||||
|
|
||||||
|
std::list<waybar::AModule*> waybar::modules::IdleInhibitor::modules;
|
||||||
|
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}"),
|
||||||
bar_(bar),
|
bar_(bar),
|
||||||
status_("deactivated"),
|
|
||||||
idle_inhibitor_(nullptr),
|
idle_inhibitor_(nullptr),
|
||||||
pid_(-1) {
|
pid_(-1) {
|
||||||
|
if (waybar::Client::inst()->idle_inhibit_manager == nullptr) {
|
||||||
|
throw std::runtime_error("idle-inhibit not available");
|
||||||
|
}
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
|
// Add this to the modules list
|
||||||
|
waybar::modules::IdleInhibitor::modules.push_back(this);
|
||||||
|
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,6 +31,10 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() {
|
|||||||
zwp_idle_inhibitor_v1_destroy(idle_inhibitor_);
|
zwp_idle_inhibitor_v1_destroy(idle_inhibitor_);
|
||||||
idle_inhibitor_ = nullptr;
|
idle_inhibitor_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove this from the modules list
|
||||||
|
waybar::modules::IdleInhibitor::modules.remove(this);
|
||||||
|
|
||||||
if (pid_ != -1) {
|
if (pid_ != -1) {
|
||||||
kill(-pid_, 9);
|
kill(-pid_, 9);
|
||||||
pid_ = -1;
|
pid_ = -1;
|
||||||
@ -26,28 +42,44 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::IdleInhibitor::update() -> void {
|
auto waybar::modules::IdleInhibitor::update() -> void {
|
||||||
label_.set_markup(
|
// Check status
|
||||||
fmt::format(format_, fmt::arg("status", status_), fmt::arg("icon", getIcon(0, status_))));
|
if (status) {
|
||||||
label_.get_style_context()->add_class(status_);
|
label_.get_style_context()->remove_class("deactivated");
|
||||||
if (tooltipEnabled()) {
|
if (idle_inhibitor_ == nullptr) {
|
||||||
label_.set_tooltip_text(status_);
|
idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor(
|
||||||
|
waybar::Client::inst()->idle_inhibit_manager, bar_.surface);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
label_.get_style_context()->remove_class("activated");
|
||||||
|
if (idle_inhibitor_ != nullptr) {
|
||||||
|
zwp_idle_inhibitor_v1_destroy(idle_inhibitor_);
|
||||||
|
idle_inhibitor_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string status_text = status ? "activated" : "deactivated";
|
||||||
|
label_.set_markup(
|
||||||
|
fmt::format(format_, fmt::arg("status", status_text), fmt::arg("icon", getIcon(0, status_text))));
|
||||||
|
label_.get_style_context()->add_class(status_text);
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
label_.set_tooltip_text(status_text);
|
||||||
|
}
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
|
bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
|
||||||
if (e->button == 1) {
|
if (e->button == 1) {
|
||||||
label_.get_style_context()->remove_class(status_);
|
status = !status;
|
||||||
if (idle_inhibitor_ != nullptr) {
|
|
||||||
zwp_idle_inhibitor_v1_destroy(idle_inhibitor_);
|
// Make all other idle inhibitor modules update
|
||||||
idle_inhibitor_ = nullptr;
|
for (auto const& module : waybar::modules::IdleInhibitor::modules) {
|
||||||
status_ = "deactivated";
|
if (module != this) {
|
||||||
} else {
|
module->update();
|
||||||
idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor(
|
|
||||||
waybar::Client::inst()->idle_inhibit_manager, bar_.surface);
|
|
||||||
status_ = "activated";
|
|
||||||
}
|
}
|
||||||
click_param = status_;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ALabel::handleToggle(e);
|
ALabel::handleToggle(e);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
#include "modules/memory.hpp"
|
|
||||||
|
|
||||||
waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config)
|
|
||||||
: ALabel(config, "memory", id, "{}%", 30) {
|
|
||||||
thread_ = [this] {
|
|
||||||
dp.emit();
|
|
||||||
thread_.sleep_for(interval_);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto waybar::modules::Memory::update() -> void {
|
|
||||||
parseMeminfo();
|
|
||||||
if (memtotal_ > 0 && memfree_ >= 0) {
|
|
||||||
auto total_ram_gigabytes = memtotal_ / std::pow(1024, 2);
|
|
||||||
int used_ram_percentage = 100 * (memtotal_ - memfree_) / memtotal_;
|
|
||||||
auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2);
|
|
||||||
auto available_ram_gigabytes = memfree_ / std::pow(1024, 2);
|
|
||||||
|
|
||||||
getState(used_ram_percentage);
|
|
||||||
label_.set_markup(fmt::format(format_,
|
|
||||||
used_ram_percentage,
|
|
||||||
fmt::arg("total", total_ram_gigabytes),
|
|
||||||
fmt::arg("percentage", used_ram_percentage),
|
|
||||||
fmt::arg("used", used_ram_gigabytes),
|
|
||||||
fmt::arg("avail", available_ram_gigabytes)));
|
|
||||||
if (tooltipEnabled()) {
|
|
||||||
label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1));
|
|
||||||
}
|
|
||||||
event_box_.show();
|
|
||||||
} else {
|
|
||||||
event_box_.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::modules::Memory::parseMeminfo() {
|
|
||||||
int64_t memfree = -1, membuffer = -1, memcache = -1, memavail = -1;
|
|
||||||
std::ifstream info(data_dir_);
|
|
||||||
if (!info.is_open()) {
|
|
||||||
throw std::runtime_error("Can't open " + data_dir_);
|
|
||||||
}
|
|
||||||
std::string line;
|
|
||||||
while (getline(info, line)) {
|
|
||||||
auto posDelim = line.find(':');
|
|
||||||
if (posDelim == std::string::npos) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::string name = line.substr(0, posDelim);
|
|
||||||
int64_t value = std::stol(line.substr(posDelim + 1));
|
|
||||||
|
|
||||||
if (name.compare("MemTotal") == 0) {
|
|
||||||
memtotal_ = value;
|
|
||||||
} else if (name.compare("MemAvailable") == 0) {
|
|
||||||
memavail = value;
|
|
||||||
} else if (name.compare("MemFree") == 0) {
|
|
||||||
memfree = value;
|
|
||||||
} else if (name.compare("Buffers") == 0) {
|
|
||||||
membuffer = value;
|
|
||||||
} else if (name.compare("Cached") == 0) {
|
|
||||||
memcache = value;
|
|
||||||
}
|
|
||||||
if (memtotal_ > 0 && (memavail >= 0 || (memfree > -1 && membuffer > -1 && memcache > -1))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memfree_ = memavail >= 0 ? memavail : memfree + membuffer + memcache;
|
|
||||||
}
|
|
89
src/modules/memory/bsd.cpp
Normal file
89
src/modules/memory/bsd.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include "modules/memory.hpp"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <unistd.h> // getpagesize
|
||||||
|
|
||||||
|
#if defined(__DragonFly__)
|
||||||
|
# include <sys/vmmeter.h> // struct vmstats
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
# include <uvm/uvm_extern.h> // struct uvmexp_sysctl
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
# include <uvm/uvmexp.h> // struct uvmexp
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint64_t get_total_memory() {
|
||||||
|
#if defined(HW_MEMSIZE) || defined(HW_PHYSMEM64)
|
||||||
|
uint64_t physmem;
|
||||||
|
#else
|
||||||
|
u_long physmem;
|
||||||
|
#endif
|
||||||
|
int mib[] = {
|
||||||
|
CTL_HW,
|
||||||
|
#if defined(HW_MEMSIZE)
|
||||||
|
HW_MEMSIZE,
|
||||||
|
#elif defined(HW_PHYSMEM64)
|
||||||
|
HW_PHYSMEM64,
|
||||||
|
#else
|
||||||
|
HW_PHYSMEM,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
u_int miblen = sizeof(mib) / sizeof(mib[0]);
|
||||||
|
size_t sz = sizeof(physmem);
|
||||||
|
if (sysctl(mib, miblen, &physmem, &sz, NULL, 0)) {
|
||||||
|
throw std::runtime_error("sysctl hw.physmem failed");
|
||||||
|
}
|
||||||
|
return physmem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t get_free_memory() {
|
||||||
|
#if defined(__DragonFly__)
|
||||||
|
struct vmstats vms;
|
||||||
|
size_t sz = sizeof(vms);
|
||||||
|
if (sysctlbyname("vm.vmstats", &vms, &sz, NULL, 0)) {
|
||||||
|
throw std::runtime_error("sysctl vm.vmstats failed");
|
||||||
|
}
|
||||||
|
return static_cast<uint64_t>
|
||||||
|
(vms.v_free_count + vms.v_inactive_count + vms.v_cache_count)
|
||||||
|
* getpagesize();
|
||||||
|
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||||
|
u_int v_free_count = 0, v_inactive_count = 0, v_cache_count = 0;
|
||||||
|
size_t sz = sizeof(u_int);
|
||||||
|
sysctlbyname("vm.stats.vm.v_free_count",
|
||||||
|
&v_free_count, &sz, NULL, 0);
|
||||||
|
sysctlbyname("vm.stats.vm.v_inactive_count",
|
||||||
|
&v_inactive_count, &sz, NULL, 0);
|
||||||
|
sysctlbyname("vm.stats.vm.v_cache_count",
|
||||||
|
&v_cache_count, &sz, NULL, 0);
|
||||||
|
return static_cast<uint64_t>
|
||||||
|
(v_free_count + v_inactive_count + v_cache_count)
|
||||||
|
* getpagesize();
|
||||||
|
#elif defined(__NetBSD__) || defined(__OpenBSD__)
|
||||||
|
#ifdef VM_UVMEXP2
|
||||||
|
# undef VM_UVMEXP
|
||||||
|
# define VM_UVMEXP VM_UVMEXP2
|
||||||
|
# define uvmexp uvmexp_sysctl
|
||||||
|
#else
|
||||||
|
# define filepages vnodepages
|
||||||
|
# define execpages vtextpages
|
||||||
|
#endif
|
||||||
|
int mib[] = {
|
||||||
|
CTL_VM,
|
||||||
|
VM_UVMEXP,
|
||||||
|
};
|
||||||
|
u_int miblen = sizeof(mib) / sizeof(mib[0]);
|
||||||
|
struct uvmexp uvmexp;
|
||||||
|
size_t sz = sizeof(uvmexp);
|
||||||
|
if (sysctl(mib, miblen, &uvmexp, &sz, NULL, 0)) {
|
||||||
|
throw std::runtime_error("sysctl vm.uvmexp failed");
|
||||||
|
}
|
||||||
|
return static_cast<uint64_t>
|
||||||
|
(uvmexp.free + uvmexp.inactive + uvmexp.filepages + uvmexp.execpages)
|
||||||
|
* uvmexp.pagesize;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::modules::Memory::parseMeminfo() {
|
||||||
|
meminfo_["MemTotal"] = get_total_memory() / 1024;
|
||||||
|
meminfo_["MemAvailable"] = get_free_memory() / 1024;
|
||||||
|
}
|
67
src/modules/memory/common.cpp
Normal file
67
src/modules/memory/common.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include "modules/memory.hpp"
|
||||||
|
|
||||||
|
waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config)
|
||||||
|
: ALabel(config, "memory", id, "{}%", 30) {
|
||||||
|
thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
thread_.sleep_for(interval_);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Memory::update() -> void {
|
||||||
|
parseMeminfo();
|
||||||
|
|
||||||
|
unsigned long memtotal = meminfo_["MemTotal"];
|
||||||
|
unsigned long memfree;
|
||||||
|
if (meminfo_.count("MemAvailable")) {
|
||||||
|
// New kernels (3.4+) have an accurate available memory field.
|
||||||
|
memfree = meminfo_["MemAvailable"];
|
||||||
|
} else {
|
||||||
|
// Old kernel; give a best-effort approximation of available memory.
|
||||||
|
memfree = meminfo_["MemFree"] + meminfo_["Buffers"] + meminfo_["Cached"] +
|
||||||
|
meminfo_["SReclaimable"] - meminfo_["Shmem"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memtotal > 0 && memfree >= 0) {
|
||||||
|
auto total_ram_gigabytes = memtotal / std::pow(1024, 2);
|
||||||
|
int used_ram_percentage = 100 * (memtotal - memfree) / memtotal;
|
||||||
|
auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2);
|
||||||
|
auto available_ram_gigabytes = memfree / std::pow(1024, 2);
|
||||||
|
|
||||||
|
auto format = format_;
|
||||||
|
auto state = getState(used_ram_percentage);
|
||||||
|
if (!state.empty() && config_["format-" + state].isString()) {
|
||||||
|
format = config_["format-" + state].asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format.empty()) {
|
||||||
|
event_box_.hide();
|
||||||
|
} else {
|
||||||
|
event_box_.show();
|
||||||
|
label_.set_markup(fmt::format(format,
|
||||||
|
used_ram_percentage,
|
||||||
|
fmt::arg("total", total_ram_gigabytes),
|
||||||
|
fmt::arg("percentage", used_ram_percentage),
|
||||||
|
fmt::arg("used", used_ram_gigabytes),
|
||||||
|
fmt::arg("avail", available_ram_gigabytes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
if (config_["tooltip-format"].isString()) {
|
||||||
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
label_.set_tooltip_text(fmt::format(tooltip_format,
|
||||||
|
used_ram_percentage,
|
||||||
|
fmt::arg("total", total_ram_gigabytes),
|
||||||
|
fmt::arg("percentage", used_ram_percentage),
|
||||||
|
fmt::arg("used", used_ram_gigabytes),
|
||||||
|
fmt::arg("avail", available_ram_gigabytes)));
|
||||||
|
} else {
|
||||||
|
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event_box_.hide();
|
||||||
|
}
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
|
}
|
20
src/modules/memory/linux.cpp
Normal file
20
src/modules/memory/linux.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "modules/memory.hpp"
|
||||||
|
|
||||||
|
void waybar::modules::Memory::parseMeminfo() {
|
||||||
|
const std::string data_dir_ = "/proc/meminfo";
|
||||||
|
std::ifstream info(data_dir_);
|
||||||
|
if (!info.is_open()) {
|
||||||
|
throw std::runtime_error("Can't open " + data_dir_);
|
||||||
|
}
|
||||||
|
std::string line;
|
||||||
|
while (getline(info, line)) {
|
||||||
|
auto posDelim = line.find(':');
|
||||||
|
if (posDelim == std::string::npos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name = line.substr(0, posDelim);
|
||||||
|
int64_t value = std::stol(line.substr(posDelim + 1));
|
||||||
|
meminfo_[name] = value;
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,23 @@
|
|||||||
#include "modules/mpd.hpp"
|
#include "modules/mpd/mpd.hpp"
|
||||||
|
|
||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <glibmm/ustring.h>
|
||||||
|
#include "modules/mpd/state.hpp"
|
||||||
|
#if defined(MPD_NOINLINE)
|
||||||
|
namespace waybar::modules {
|
||||||
|
#include "modules/mpd/state.inl.hpp"
|
||||||
|
} // namespace waybar::modules
|
||||||
|
#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),
|
||||||
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),
|
||||||
|
password_(config_["password"].empty() ? "" : config_["password"].asString()),
|
||||||
timeout_(config_["timeout"].isUInt() ? config_["timeout"].asUInt() * 1'000 : 30'000),
|
timeout_(config_["timeout"].isUInt() ? config_["timeout"].asUInt() * 1'000 : 30'000),
|
||||||
connection_(nullptr, &mpd_connection_free),
|
connection_(nullptr, &mpd_connection_free),
|
||||||
alternate_connection_(nullptr, &mpd_connection_free),
|
|
||||||
status_(nullptr, &mpd_status_free),
|
status_(nullptr, &mpd_status_free),
|
||||||
song_(nullptr, &mpd_song_free) {
|
song_(nullptr, &mpd_song_free) {
|
||||||
if (!config_["port"].isNull() && !config_["port"].isUInt()) {
|
if (!config_["port"].isNull() && !config_["port"].isUInt()) {
|
||||||
@ -28,66 +35,33 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
|
|||||||
server_ = config["server"].asCString();
|
server_ = config["server"].asCString();
|
||||||
}
|
}
|
||||||
|
|
||||||
event_listener().detach();
|
|
||||||
|
|
||||||
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
|
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||||
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &MPD::handlePlayPause));
|
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &MPD::handlePlayPause));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::MPD::update() -> void {
|
auto waybar::modules::MPD::update() -> void {
|
||||||
std::lock_guard guard(connection_lock_);
|
context_.update();
|
||||||
tryConnect();
|
|
||||||
|
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::modules::MPD::queryMPD() {
|
||||||
if (connection_ != nullptr) {
|
if (connection_ != nullptr) {
|
||||||
|
spdlog::debug("{}: fetching state information", module_name_);
|
||||||
try {
|
try {
|
||||||
bool wasPlaying = playing();
|
|
||||||
if(!wasPlaying) {
|
|
||||||
// Wait until the periodic_updater has stopped
|
|
||||||
std::lock_guard periodic_guard(periodic_lock_);
|
|
||||||
}
|
|
||||||
fetchState();
|
fetchState();
|
||||||
if (!wasPlaying && playing()) {
|
spdlog::debug("{}: fetch complete", module_name_);
|
||||||
periodic_updater().detach();
|
} catch (std::exception const& e) {
|
||||||
}
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
spdlog::error("{}: {}", module_name_, e.what());
|
spdlog::error("{}: {}", module_name_, e.what());
|
||||||
state_ = MPD_STATE_UNKNOWN;
|
state_ = MPD_STATE_UNKNOWN;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
setLabel();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::thread waybar::modules::MPD::event_listener() {
|
|
||||||
return std::thread([this] {
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
if (connection_ == nullptr) {
|
|
||||||
// Retry periodically if no connection
|
|
||||||
dp.emit();
|
|
||||||
std::this_thread::sleep_for(interval_);
|
|
||||||
} else {
|
|
||||||
waitForEvent();
|
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
|
||||||
spdlog::warn("{}: {}", module_name_, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread waybar::modules::MPD::periodic_updater() {
|
std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) const {
|
||||||
return std::thread([this] {
|
|
||||||
std::lock_guard guard(periodic_lock_);
|
|
||||||
while (connection_ != nullptr && playing()) {
|
|
||||||
dp.emit();
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) {
|
|
||||||
std::string result =
|
std::string result =
|
||||||
config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A";
|
config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A";
|
||||||
const char* tag = mpd_song_get_tag(song_.get(), type, idx);
|
const char* tag = mpd_song_get_tag(song_.get(), type, idx);
|
||||||
@ -124,8 +98,9 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
|
Glib::ustring artist, album_artist, album, title;
|
||||||
std::string artist, album_artist, album, title, date;
|
std::string date;
|
||||||
|
int song_pos = 0, queue_length = 0;
|
||||||
std::chrono::seconds elapsedTime, totalTime;
|
std::chrono::seconds elapsedTime, totalTime;
|
||||||
|
|
||||||
std::string stateIcon = "";
|
std::string stateIcon = "";
|
||||||
@ -141,8 +116,8 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
label_.get_style_context()->add_class("playing");
|
label_.get_style_context()->add_class("playing");
|
||||||
label_.get_style_context()->remove_class("paused");
|
label_.get_style_context()->remove_class("paused");
|
||||||
} else if (paused()) {
|
} else if (paused()) {
|
||||||
format =
|
format = config_["format-paused"].isString() ? config_["format-paused"].asString()
|
||||||
config_["format-paused"].isString() ? config_["format-paused"].asString() : config_["format"].asString();
|
: config_["format"].asString();
|
||||||
label_.get_style_context()->add_class("paused");
|
label_.get_style_context()->add_class("paused");
|
||||||
label_.get_style_context()->remove_class("playing");
|
label_.get_style_context()->remove_class("playing");
|
||||||
}
|
}
|
||||||
@ -154,6 +129,8 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
album = getTag(MPD_TAG_ALBUM);
|
album = getTag(MPD_TAG_ALBUM);
|
||||||
title = getTag(MPD_TAG_TITLE);
|
title = getTag(MPD_TAG_TITLE);
|
||||||
date = getTag(MPD_TAG_DATE);
|
date = getTag(MPD_TAG_DATE);
|
||||||
|
song_pos = mpd_status_get_song_pos(status_.get());
|
||||||
|
queue_length = mpd_status_get_queue_length(status_.get());
|
||||||
elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get()));
|
elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get()));
|
||||||
totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get()));
|
totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get()));
|
||||||
}
|
}
|
||||||
@ -166,8 +143,12 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
std::string repeatIcon = getOptionIcon("repeat", repeatActivated);
|
std::string repeatIcon = getOptionIcon("repeat", repeatActivated);
|
||||||
bool singleActivated = mpd_status_get_single(status_.get());
|
bool singleActivated = mpd_status_get_single(status_.get());
|
||||||
std::string singleIcon = getOptionIcon("single", singleActivated);
|
std::string singleIcon = getOptionIcon("single", singleActivated);
|
||||||
|
if (config_["artist-len"].isInt()) artist = artist.substr(0, config_["artist-len"].asInt());
|
||||||
|
if (config_["album-artist-len"].isInt()) album_artist = album_artist.substr(0, config_["album-artist-len"].asInt());
|
||||||
|
if (config_["album-len"].isInt()) album = album.substr(0, config_["album-len"].asInt());
|
||||||
|
if (config_["title-len"].isInt()) title = title.substr(0,config_["title-len"].asInt());
|
||||||
|
|
||||||
// TODO: format can fail
|
try {
|
||||||
label_.set_markup(
|
label_.set_markup(
|
||||||
fmt::format(format,
|
fmt::format(format,
|
||||||
fmt::arg("artist", Glib::Markup::escape_text(artist).raw()),
|
fmt::arg("artist", Glib::Markup::escape_text(artist).raw()),
|
||||||
@ -177,32 +158,45 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
fmt::arg("date", Glib::Markup::escape_text(date).raw()),
|
fmt::arg("date", Glib::Markup::escape_text(date).raw()),
|
||||||
fmt::arg("elapsedTime", elapsedTime),
|
fmt::arg("elapsedTime", elapsedTime),
|
||||||
fmt::arg("totalTime", totalTime),
|
fmt::arg("totalTime", totalTime),
|
||||||
|
fmt::arg("songPosition", song_pos),
|
||||||
|
fmt::arg("queueLength", queue_length),
|
||||||
fmt::arg("stateIcon", stateIcon),
|
fmt::arg("stateIcon", stateIcon),
|
||||||
fmt::arg("consumeIcon", consumeIcon),
|
fmt::arg("consumeIcon", consumeIcon),
|
||||||
fmt::arg("randomIcon", randomIcon),
|
fmt::arg("randomIcon", randomIcon),
|
||||||
fmt::arg("repeatIcon", repeatIcon),
|
fmt::arg("repeatIcon", repeatIcon),
|
||||||
fmt::arg("singleIcon", singleIcon)));
|
fmt::arg("singleIcon", singleIcon)));
|
||||||
|
} catch (fmt::format_error const& e) {
|
||||||
|
spdlog::warn("mpd: format error: {}", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
std::string tooltip_format;
|
std::string tooltip_format;
|
||||||
tooltip_format = config_["tooltip-format"].isString() ? config_["tooltip-format"].asString()
|
tooltip_format = config_["tooltip-format"].isString() ? config_["tooltip-format"].asString()
|
||||||
: "MPD (connected)";
|
: "MPD (connected)";
|
||||||
|
try {
|
||||||
auto tooltip_text = fmt::format(tooltip_format,
|
auto tooltip_text = fmt::format(tooltip_format,
|
||||||
fmt::arg("artist", artist),
|
fmt::arg("artist", artist.raw()),
|
||||||
fmt::arg("albumArtist", album_artist),
|
fmt::arg("albumArtist", album_artist.raw()),
|
||||||
fmt::arg("album", album),
|
fmt::arg("album", album.raw()),
|
||||||
fmt::arg("title", title),
|
fmt::arg("title", title.raw()),
|
||||||
fmt::arg("date", date),
|
fmt::arg("date", date),
|
||||||
|
fmt::arg("elapsedTime", elapsedTime),
|
||||||
|
fmt::arg("totalTime", totalTime),
|
||||||
|
fmt::arg("songPosition", song_pos),
|
||||||
|
fmt::arg("queueLength", queue_length),
|
||||||
fmt::arg("stateIcon", stateIcon),
|
fmt::arg("stateIcon", stateIcon),
|
||||||
fmt::arg("consumeIcon", consumeIcon),
|
fmt::arg("consumeIcon", consumeIcon),
|
||||||
fmt::arg("randomIcon", randomIcon),
|
fmt::arg("randomIcon", randomIcon),
|
||||||
fmt::arg("repeatIcon", repeatIcon),
|
fmt::arg("repeatIcon", repeatIcon),
|
||||||
fmt::arg("singleIcon", singleIcon));
|
fmt::arg("singleIcon", singleIcon));
|
||||||
label_.set_tooltip_text(tooltip_text);
|
label_.set_tooltip_text(tooltip_text);
|
||||||
|
} catch (fmt::format_error const& e) {
|
||||||
|
spdlog::warn("mpd: format error (tooltip): {}", e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string waybar::modules::MPD::getStateIcon() {
|
std::string waybar::modules::MPD::getStateIcon() const {
|
||||||
if (!config_["state-icons"].isObject()) {
|
if (!config_["state-icons"].isObject()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -224,7 +218,7 @@ std::string waybar::modules::MPD::getStateIcon() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) {
|
std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) const {
|
||||||
if (!config_[optionName + "-icons"].isObject()) {
|
if (!config_[optionName + "-icons"].isObject()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -247,25 +241,30 @@ void waybar::modules::MPD::tryConnect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connection_ =
|
connection_ =
|
||||||
unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
|
detail::unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
|
||||||
|
|
||||||
alternate_connection_ =
|
if (connection_ == nullptr) {
|
||||||
unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
|
|
||||||
|
|
||||||
if (connection_ == nullptr || alternate_connection_ == nullptr) {
|
|
||||||
spdlog::error("{}: Failed to connect to MPD", module_name_);
|
spdlog::error("{}: Failed to connect to MPD", module_name_);
|
||||||
connection_.reset();
|
connection_.reset();
|
||||||
alternate_connection_.reset();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
checkErrors(connection_.get());
|
checkErrors(connection_.get());
|
||||||
spdlog::info("{}: Connected to MPD", module_name_);
|
spdlog::debug("{}: Connected to MPD", module_name_);
|
||||||
|
|
||||||
|
if (!password_.empty()) {
|
||||||
|
bool res = mpd_run_password(connection_.get(), password_.c_str());
|
||||||
|
if (!res) {
|
||||||
|
spdlog::error("{}: Wrong MPD password", module_name_);
|
||||||
|
connection_.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkErrors(connection_.get());
|
||||||
|
}
|
||||||
} catch (std::runtime_error& e) {
|
} catch (std::runtime_error& e) {
|
||||||
spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what());
|
spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what());
|
||||||
connection_.reset();
|
connection_.reset();
|
||||||
alternate_connection_.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,48 +277,34 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) {
|
|||||||
case MPD_ERROR_CLOSED:
|
case MPD_ERROR_CLOSED:
|
||||||
mpd_connection_clear_error(conn);
|
mpd_connection_clear_error(conn);
|
||||||
connection_.reset();
|
connection_.reset();
|
||||||
alternate_connection_.reset();
|
|
||||||
state_ = MPD_STATE_UNKNOWN;
|
state_ = MPD_STATE_UNKNOWN;
|
||||||
throw std::runtime_error("Connection to MPD closed");
|
throw std::runtime_error("Connection to MPD closed");
|
||||||
default:
|
default:
|
||||||
|
if (conn) {
|
||||||
auto error_message = mpd_connection_get_error_message(conn);
|
auto error_message = mpd_connection_get_error_message(conn);
|
||||||
|
std::string error(error_message);
|
||||||
mpd_connection_clear_error(conn);
|
mpd_connection_clear_error(conn);
|
||||||
throw std::runtime_error(std::string(error_message));
|
throw std::runtime_error(error);
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Invalid connection");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::MPD::fetchState() {
|
void waybar::modules::MPD::fetchState() {
|
||||||
|
if (connection_ == nullptr) {
|
||||||
|
spdlog::error("{}: Not connected to MPD", module_name_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto conn = connection_.get();
|
auto conn = connection_.get();
|
||||||
status_ = unique_status(mpd_run_status(conn), &mpd_status_free);
|
|
||||||
|
status_ = detail::unique_status(mpd_run_status(conn), &mpd_status_free);
|
||||||
checkErrors(conn);
|
checkErrors(conn);
|
||||||
|
|
||||||
state_ = mpd_status_get_state(status_.get());
|
state_ = mpd_status_get_state(status_.get());
|
||||||
checkErrors(conn);
|
checkErrors(conn);
|
||||||
|
|
||||||
song_ = unique_song(mpd_run_current_song(conn), &mpd_song_free);
|
song_ = detail::unique_song(mpd_run_current_song(conn), &mpd_song_free);
|
||||||
checkErrors(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::modules::MPD::waitForEvent() {
|
|
||||||
auto conn = alternate_connection_.get();
|
|
||||||
// Wait for a player (play/pause), option (random, shuffle, etc.), or playlist
|
|
||||||
// change
|
|
||||||
if (!mpd_send_idle_mask(
|
|
||||||
conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) {
|
|
||||||
checkErrors(conn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// alternate_idle_ = true;
|
|
||||||
|
|
||||||
// See issue #277:
|
|
||||||
// https://github.com/Alexays/Waybar/issues/277
|
|
||||||
mpd_recv_idle(conn, /* disable_timeout = */ false);
|
|
||||||
// See issue #281:
|
|
||||||
// https://github.com/Alexays/Waybar/issues/281
|
|
||||||
std::lock_guard guard(connection_lock_);
|
|
||||||
|
|
||||||
checkErrors(conn);
|
|
||||||
mpd_response_finish(conn);
|
|
||||||
|
|
||||||
checkErrors(conn);
|
checkErrors(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,24 +314,13 @@ bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (e->button == 1) {
|
if (e->button == 1) {
|
||||||
std::lock_guard guard(connection_lock_);
|
if (state_ == MPD_STATE_PLAY)
|
||||||
if (stopped()) {
|
context_.pause();
|
||||||
mpd_run_play(connection_.get());
|
else
|
||||||
} else {
|
context_.play();
|
||||||
mpd_run_toggle_pause(connection_.get());
|
|
||||||
}
|
|
||||||
} else if (e->button == 3) {
|
} else if (e->button == 3) {
|
||||||
std::lock_guard guard(connection_lock_);
|
context_.stop();
|
||||||
mpd_run_stop(connection_.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::modules::MPD::stopped() {
|
|
||||||
return connection_ == nullptr || state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; }
|
|
||||||
|
|
||||||
bool waybar::modules::MPD::paused() { return connection_ != nullptr && state_ == MPD_STATE_PAUSE; }
|
|
383
src/modules/mpd/state.cpp
Normal file
383
src/modules/mpd/state.cpp
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
#include "modules/mpd/state.hpp"
|
||||||
|
|
||||||
|
#include <fmt/chrono.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include "modules/mpd/mpd.hpp"
|
||||||
|
#if defined(MPD_NOINLINE)
|
||||||
|
namespace waybar::modules {
|
||||||
|
#include "modules/mpd/state.inl.hpp"
|
||||||
|
} // namespace waybar::modules
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace waybar::modules::detail {
|
||||||
|
|
||||||
|
#define IDLE_RUN_NOIDLE_AND_CMD(...) \
|
||||||
|
if (idle_connection_.connected()) { \
|
||||||
|
idle_connection_.disconnect(); \
|
||||||
|
auto conn = ctx_->connection().get(); \
|
||||||
|
if (!mpd_run_noidle(conn)) { \
|
||||||
|
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { \
|
||||||
|
spdlog::error("mpd: Idle: failed to unregister for IDLE events"); \
|
||||||
|
ctx_->checkErrors(conn); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
__VA_ARGS__; \
|
||||||
|
}
|
||||||
|
|
||||||
|
void Idle::play() {
|
||||||
|
IDLE_RUN_NOIDLE_AND_CMD(mpd_run_play(conn));
|
||||||
|
|
||||||
|
ctx_->setState(std::make_unique<Playing>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Idle::pause() {
|
||||||
|
IDLE_RUN_NOIDLE_AND_CMD(mpd_run_pause(conn, true));
|
||||||
|
|
||||||
|
ctx_->setState(std::make_unique<Paused>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Idle::stop() {
|
||||||
|
IDLE_RUN_NOIDLE_AND_CMD(mpd_run_stop(conn));
|
||||||
|
|
||||||
|
ctx_->setState(std::make_unique<Stopped>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef IDLE_RUN_NOIDLE_AND_CMD
|
||||||
|
|
||||||
|
void Idle::update() noexcept {
|
||||||
|
// This is intentionally blank.
|
||||||
|
}
|
||||||
|
|
||||||
|
void Idle::entry() noexcept {
|
||||||
|
auto conn = ctx_->connection().get();
|
||||||
|
assert(conn != nullptr);
|
||||||
|
|
||||||
|
if (!mpd_send_idle_mask(
|
||||||
|
conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) {
|
||||||
|
ctx_->checkErrors(conn);
|
||||||
|
spdlog::error("mpd: Idle: failed to register for IDLE events");
|
||||||
|
} else {
|
||||||
|
spdlog::debug("mpd: Idle: watching FD");
|
||||||
|
sigc::slot<bool, Glib::IOCondition const&> idle_slot = sigc::mem_fun(*this, &Idle::on_io);
|
||||||
|
idle_connection_ =
|
||||||
|
Glib::signal_io().connect(idle_slot,
|
||||||
|
mpd_connection_get_fd(conn),
|
||||||
|
Glib::IO_IN | Glib::IO_PRI | Glib::IO_ERR | Glib::IO_HUP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Idle::exit() noexcept {
|
||||||
|
if (idle_connection_.connected()) {
|
||||||
|
idle_connection_.disconnect();
|
||||||
|
spdlog::debug("mpd: Idle: unwatching FD");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Idle::on_io(Glib::IOCondition const&) {
|
||||||
|
auto conn = ctx_->connection().get();
|
||||||
|
|
||||||
|
// callback should do this:
|
||||||
|
enum mpd_idle events = mpd_recv_idle(conn, /* ignore_timeout?= */ false);
|
||||||
|
spdlog::debug("mpd: Idle: recv_idle events -> {}", events);
|
||||||
|
|
||||||
|
mpd_response_finish(conn);
|
||||||
|
try {
|
||||||
|
ctx_->checkErrors(conn);
|
||||||
|
} catch (std::exception const& e) {
|
||||||
|
spdlog::warn("mpd: Idle: error: {}", e.what());
|
||||||
|
ctx_->setState(std::make_unique<Disconnected>(ctx_));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->fetchState();
|
||||||
|
mpd_state state = ctx_->state();
|
||||||
|
|
||||||
|
if (state == MPD_STATE_STOP) {
|
||||||
|
ctx_->emit();
|
||||||
|
ctx_->setState(std::make_unique<Stopped>(ctx_));
|
||||||
|
} else if (state == MPD_STATE_PLAY) {
|
||||||
|
ctx_->emit();
|
||||||
|
ctx_->setState(std::make_unique<Playing>(ctx_));
|
||||||
|
} else if (state == MPD_STATE_PAUSE) {
|
||||||
|
ctx_->emit();
|
||||||
|
ctx_->setState(std::make_unique<Paused>(ctx_));
|
||||||
|
} else {
|
||||||
|
ctx_->emit();
|
||||||
|
// self transition
|
||||||
|
ctx_->setState(std::make_unique<Idle>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Playing::entry() noexcept {
|
||||||
|
sigc::slot<bool> timer_slot = sigc::mem_fun(*this, &Playing::on_timer);
|
||||||
|
timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 1'000);
|
||||||
|
spdlog::debug("mpd: Playing: enabled 1 second periodic timer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Playing::exit() noexcept {
|
||||||
|
if (timer_connection_.connected()) {
|
||||||
|
timer_connection_.disconnect();
|
||||||
|
spdlog::debug("mpd: Playing: disabled 1 second periodic timer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Playing::on_timer() {
|
||||||
|
// Attempt to connect with MPD.
|
||||||
|
try {
|
||||||
|
ctx_->tryConnect();
|
||||||
|
|
||||||
|
// Success?
|
||||||
|
if (!ctx_->is_connected()) {
|
||||||
|
ctx_->setState(std::make_unique<Disconnected>(ctx_));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->fetchState();
|
||||||
|
|
||||||
|
if (!ctx_->is_playing()) {
|
||||||
|
if (ctx_->is_paused()) {
|
||||||
|
ctx_->setState(std::make_unique<Paused>(ctx_));
|
||||||
|
} else {
|
||||||
|
ctx_->setState(std::make_unique<Stopped>(ctx_));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->queryMPD();
|
||||||
|
ctx_->emit();
|
||||||
|
} catch (std::exception const& e) {
|
||||||
|
spdlog::warn("mpd: Playing: error: {}", e.what());
|
||||||
|
ctx_->setState(std::make_unique<Disconnected>(ctx_));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Playing::stop() {
|
||||||
|
if (timer_connection_.connected()) {
|
||||||
|
timer_connection_.disconnect();
|
||||||
|
|
||||||
|
mpd_run_stop(ctx_->connection().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->setState(std::make_unique<Stopped>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Playing::pause() {
|
||||||
|
if (timer_connection_.connected()) {
|
||||||
|
timer_connection_.disconnect();
|
||||||
|
|
||||||
|
mpd_run_pause(ctx_->connection().get(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->setState(std::make_unique<Paused>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Playing::update() noexcept { ctx_->do_update(); }
|
||||||
|
|
||||||
|
void Paused::entry() noexcept {
|
||||||
|
sigc::slot<bool> timer_slot = sigc::mem_fun(*this, &Paused::on_timer);
|
||||||
|
timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200);
|
||||||
|
spdlog::debug("mpd: Paused: enabled 200 ms periodic timer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Paused::exit() noexcept {
|
||||||
|
if (timer_connection_.connected()) {
|
||||||
|
timer_connection_.disconnect();
|
||||||
|
spdlog::debug("mpd: Paused: disabled 200 ms periodic timer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Paused::on_timer() {
|
||||||
|
bool rc = true;
|
||||||
|
|
||||||
|
// Attempt to connect with MPD.
|
||||||
|
try {
|
||||||
|
ctx_->tryConnect();
|
||||||
|
|
||||||
|
// Success?
|
||||||
|
if (!ctx_->is_connected()) {
|
||||||
|
ctx_->setState(std::make_unique<Disconnected>(ctx_));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->fetchState();
|
||||||
|
|
||||||
|
ctx_->emit();
|
||||||
|
|
||||||
|
if (ctx_->is_paused()) {
|
||||||
|
ctx_->setState(std::make_unique<Idle>(ctx_));
|
||||||
|
rc = false;
|
||||||
|
} else if (ctx_->is_playing()) {
|
||||||
|
ctx_->setState(std::make_unique<Playing>(ctx_));
|
||||||
|
rc = false;
|
||||||
|
} else if (ctx_->is_stopped()) {
|
||||||
|
ctx_->setState(std::make_unique<Stopped>(ctx_));
|
||||||
|
rc = false;
|
||||||
|
}
|
||||||
|
} catch (std::exception const& e) {
|
||||||
|
spdlog::warn("mpd: Paused: error: {}", e.what());
|
||||||
|
ctx_->setState(std::make_unique<Disconnected>(ctx_));
|
||||||
|
rc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Paused::play() {
|
||||||
|
if (timer_connection_.connected()) {
|
||||||
|
timer_connection_.disconnect();
|
||||||
|
|
||||||
|
mpd_run_play(ctx_->connection().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->setState(std::make_unique<Playing>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Paused::stop() {
|
||||||
|
if (timer_connection_.connected()) {
|
||||||
|
timer_connection_.disconnect();
|
||||||
|
|
||||||
|
mpd_run_stop(ctx_->connection().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->setState(std::make_unique<Stopped>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Paused::update() noexcept { ctx_->do_update(); }
|
||||||
|
|
||||||
|
void Stopped::entry() noexcept {
|
||||||
|
sigc::slot<bool> timer_slot = sigc::mem_fun(*this, &Stopped::on_timer);
|
||||||
|
timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200);
|
||||||
|
spdlog::debug("mpd: Stopped: enabled 200 ms periodic timer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stopped::exit() noexcept {
|
||||||
|
if (timer_connection_.connected()) {
|
||||||
|
timer_connection_.disconnect();
|
||||||
|
spdlog::debug("mpd: Stopped: disabled 200 ms periodic timer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stopped::on_timer() {
|
||||||
|
bool rc = true;
|
||||||
|
|
||||||
|
// Attempt to connect with MPD.
|
||||||
|
try {
|
||||||
|
ctx_->tryConnect();
|
||||||
|
|
||||||
|
// Success?
|
||||||
|
if (!ctx_->is_connected()) {
|
||||||
|
ctx_->setState(std::make_unique<Disconnected>(ctx_));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->fetchState();
|
||||||
|
|
||||||
|
ctx_->emit();
|
||||||
|
|
||||||
|
if (ctx_->is_stopped()) {
|
||||||
|
ctx_->setState(std::make_unique<Idle>(ctx_));
|
||||||
|
rc = false;
|
||||||
|
} else if (ctx_->is_playing()) {
|
||||||
|
ctx_->setState(std::make_unique<Playing>(ctx_));
|
||||||
|
rc = false;
|
||||||
|
} else if (ctx_->is_paused()) {
|
||||||
|
ctx_->setState(std::make_unique<Paused>(ctx_));
|
||||||
|
rc = false;
|
||||||
|
}
|
||||||
|
} catch (std::exception const& e) {
|
||||||
|
spdlog::warn("mpd: Stopped: error: {}", e.what());
|
||||||
|
ctx_->setState(std::make_unique<Disconnected>(ctx_));
|
||||||
|
rc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stopped::play() {
|
||||||
|
if (timer_connection_.connected()) {
|
||||||
|
timer_connection_.disconnect();
|
||||||
|
|
||||||
|
mpd_run_play(ctx_->connection().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->setState(std::make_unique<Playing>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stopped::pause() {
|
||||||
|
if (timer_connection_.connected()) {
|
||||||
|
timer_connection_.disconnect();
|
||||||
|
|
||||||
|
mpd_run_pause(ctx_->connection().get(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_->setState(std::make_unique<Paused>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stopped::update() noexcept { ctx_->do_update(); }
|
||||||
|
|
||||||
|
void Disconnected::arm_timer(int interval) noexcept {
|
||||||
|
// unregister timer, if present
|
||||||
|
disarm_timer();
|
||||||
|
|
||||||
|
// register timer
|
||||||
|
sigc::slot<bool> timer_slot = sigc::mem_fun(*this, &Disconnected::on_timer);
|
||||||
|
timer_connection_ =
|
||||||
|
Glib::signal_timeout().connect(timer_slot, interval);
|
||||||
|
spdlog::debug("mpd: Disconnected: enabled interval timer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disconnected::disarm_timer() noexcept {
|
||||||
|
// unregister timer, if present
|
||||||
|
if (timer_connection_.connected()) {
|
||||||
|
timer_connection_.disconnect();
|
||||||
|
spdlog::debug("mpd: Disconnected: disabled interval timer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disconnected::entry() noexcept {
|
||||||
|
ctx_->emit();
|
||||||
|
arm_timer(1'000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disconnected::exit() noexcept {
|
||||||
|
disarm_timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Disconnected::on_timer() {
|
||||||
|
// Attempt to connect with MPD.
|
||||||
|
try {
|
||||||
|
ctx_->tryConnect();
|
||||||
|
|
||||||
|
// Success?
|
||||||
|
if (ctx_->is_connected()) {
|
||||||
|
ctx_->fetchState();
|
||||||
|
ctx_->emit();
|
||||||
|
|
||||||
|
if (ctx_->is_playing()) {
|
||||||
|
ctx_->setState(std::make_unique<Playing>(ctx_));
|
||||||
|
} else if (ctx_->is_paused()) {
|
||||||
|
ctx_->setState(std::make_unique<Paused>(ctx_));
|
||||||
|
} else {
|
||||||
|
ctx_->setState(std::make_unique<Stopped>(ctx_));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // do not rearm timer
|
||||||
|
}
|
||||||
|
} catch (std::exception const& e) {
|
||||||
|
spdlog::warn("mpd: Disconnected: error: {}", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
arm_timer(ctx_->interval() * 1'000);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disconnected::update() noexcept { ctx_->do_update(); }
|
||||||
|
|
||||||
|
} // namespace waybar::modules::detail
|
@ -3,8 +3,11 @@
|
|||||||
#include <sys/eventfd.h>
|
#include <sys/eventfd.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <optional>
|
||||||
#include "util/format.hpp"
|
#include "util/format.hpp"
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
#include "util/rfkill.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -86,6 +89,9 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
|||||||
cidr_(-1),
|
cidr_(-1),
|
||||||
signal_strength_dbm_(0),
|
signal_strength_dbm_(0),
|
||||||
signal_strength_(0),
|
signal_strength_(0),
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
rfkill_{RFKILL_TYPE_WLAN},
|
||||||
|
#endif
|
||||||
frequency_(0) {
|
frequency_(0) {
|
||||||
auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
|
auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
|
||||||
auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
|
auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
|
||||||
@ -117,8 +123,6 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
|||||||
|
|
||||||
waybar::modules::Network::~Network() {
|
waybar::modules::Network::~Network() {
|
||||||
if (ev_fd_ > -1) {
|
if (ev_fd_ > -1) {
|
||||||
eventfd_write(ev_fd_, 1);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
|
||||||
close(ev_fd_);
|
close(ev_fd_);
|
||||||
}
|
}
|
||||||
if (efd_ > -1) {
|
if (efd_ > -1) {
|
||||||
@ -191,11 +195,12 @@ void waybar::modules::Network::createInfoSocket() {
|
|||||||
}
|
}
|
||||||
nl80211_id_ = genl_ctrl_resolve(sock_, "nl80211");
|
nl80211_id_ = genl_ctrl_resolve(sock_, "nl80211");
|
||||||
if (nl80211_id_ < 0) {
|
if (nl80211_id_ < 0) {
|
||||||
throw std::runtime_error("Can't resolve nl80211 interface");
|
spdlog::warn("Can't resolve nl80211 interface");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Network::worker() {
|
void waybar::modules::Network::worker() {
|
||||||
|
// update via here not working
|
||||||
thread_timer_ = [this] {
|
thread_timer_ = [this] {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
@ -206,6 +211,17 @@ void waybar::modules::Network::worker() {
|
|||||||
}
|
}
|
||||||
thread_timer_.sleep_for(interval_);
|
thread_timer_.sleep_for(interval_);
|
||||||
};
|
};
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
rfkill_.on_update.connect([this](auto &) {
|
||||||
|
/* If we are here, it's likely that the network thread already holds the mutex and will be
|
||||||
|
* holding it for a next few seconds.
|
||||||
|
* Let's delegate the update to the timer thread instead of blocking the main thread.
|
||||||
|
*/
|
||||||
|
thread_timer_.wake_up();
|
||||||
|
});
|
||||||
|
#else
|
||||||
|
spdlog::warn("Waybar has been built without rfkill support.");
|
||||||
|
#endif
|
||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
std::array<struct epoll_event, EPOLL_MAX> events{};
|
std::array<struct epoll_event, EPOLL_MAX> events{};
|
||||||
|
|
||||||
@ -222,7 +238,13 @@ void waybar::modules::Network::worker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string waybar::modules::Network::getNetworkState() const {
|
const std::string waybar::modules::Network::getNetworkState() const {
|
||||||
if (ifid_ == -1) return "disconnected";
|
if (ifid_ == -1) {
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
if (rfkill_.getState())
|
||||||
|
return "disabled";
|
||||||
|
#endif
|
||||||
|
return "disconnected";
|
||||||
|
}
|
||||||
if (ipaddr_.empty()) return "linked";
|
if (ipaddr_.empty()) return "linked";
|
||||||
if (essid_.empty()) return "ethernet";
|
if (essid_.empty()) return "ethernet";
|
||||||
return "wifi";
|
return "wifi";
|
||||||
@ -279,7 +301,7 @@ auto waybar::modules::Network::update() -> void {
|
|||||||
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("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")));
|
||||||
if (text != label_.get_label()) {
|
if (text.compare(label_.get_label()) != 0) {
|
||||||
label_.set_markup(text);
|
label_.set_markup(text);
|
||||||
if (text.empty()) {
|
if (text.empty()) {
|
||||||
event_box_.hide();
|
event_box_.hide();
|
||||||
@ -308,13 +330,16 @@ auto waybar::modules::Network::update() -> void {
|
|||||||
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("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")));
|
||||||
if (label_.get_tooltip_text() != text) {
|
if (label_.get_tooltip_text() != tooltip_text) {
|
||||||
label_.set_tooltip_text(tooltip_text);
|
label_.set_tooltip_text(tooltip_text);
|
||||||
}
|
}
|
||||||
} else if (label_.get_tooltip_text() != text) {
|
} else if (label_.get_tooltip_text() != text) {
|
||||||
label_.set_tooltip_text(text);
|
label_.set_tooltip_text(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
|
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
|
||||||
|
@ -21,7 +21,7 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value
|
|||||||
if (context_ == nullptr) {
|
if (context_ == nullptr) {
|
||||||
throw std::runtime_error("pa_context_new() failed.");
|
throw std::runtime_error("pa_context_new() failed.");
|
||||||
}
|
}
|
||||||
if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOAUTOSPAWN, nullptr) < 0) {
|
if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) {
|
||||||
auto err =
|
auto err =
|
||||||
fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(context_)));
|
fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(context_)));
|
||||||
throw std::runtime_error(err);
|
throw std::runtime_error(err);
|
||||||
@ -52,7 +52,8 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) {
|
|||||||
pa_context_set_subscribe_callback(c, subscribeCb, data);
|
pa_context_set_subscribe_callback(c, subscribeCb, data);
|
||||||
pa_context_subscribe(
|
pa_context_subscribe(
|
||||||
c,
|
c,
|
||||||
static_cast<enum pa_subscription_mask>(static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) |
|
static_cast<enum pa_subscription_mask>(static_cast<int>(PA_SUBSCRIPTION_MASK_SERVER) |
|
||||||
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) |
|
||||||
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE)),
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE)),
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr);
|
nullptr);
|
||||||
@ -109,7 +110,9 @@ void waybar::modules::Pulseaudio::subscribeCb(pa_context * conte
|
|||||||
if (operation != PA_SUBSCRIPTION_EVENT_CHANGE) {
|
if (operation != PA_SUBSCRIPTION_EVENT_CHANGE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
|
if (facility == PA_SUBSCRIPTION_EVENT_SERVER) {
|
||||||
|
pa_context_get_server_info(context, serverInfoCb, data);
|
||||||
|
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
|
||||||
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
|
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
|
||||||
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) {
|
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) {
|
||||||
pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data);
|
pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data);
|
||||||
@ -131,15 +134,15 @@ void waybar::modules::Pulseaudio::volumeModifyCb(pa_context *c, int success, voi
|
|||||||
*/
|
*/
|
||||||
void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i,
|
void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i,
|
||||||
int /*eol*/, void *data) {
|
int /*eol*/, void *data) {
|
||||||
if (i != nullptr) {
|
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
||||||
auto self = static_cast<waybar::modules::Pulseaudio *>(data);
|
if (i != nullptr && pa->default_source_name_ == i->name) {
|
||||||
auto source_volume = static_cast<float>(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM};
|
auto source_volume = static_cast<float>(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM};
|
||||||
self->source_volume_ = std::round(source_volume * 100.0F);
|
pa->source_volume_ = std::round(source_volume * 100.0F);
|
||||||
self->source_idx_ = i->index;
|
pa->source_idx_ = i->index;
|
||||||
self->source_muted_ = i->mute != 0;
|
pa->source_muted_ = i->mute != 0;
|
||||||
self->source_desc_ = i->description;
|
pa->source_desc_ = i->description;
|
||||||
self->source_port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
|
pa->source_port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
|
||||||
self->dp.emit();
|
pa->dp.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,9 +150,9 @@ void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const p
|
|||||||
* Called when the requested sink information is ready.
|
* Called when the requested sink information is ready.
|
||||||
*/
|
*/
|
||||||
void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i,
|
void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i,
|
||||||
int /*eol*/, void * data) {
|
int /*eol*/, void *data) {
|
||||||
if (i != nullptr) {
|
|
||||||
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
||||||
|
if (i != nullptr && pa->default_sink_name_ == i->name) {
|
||||||
pa->pa_volume_ = i->volume;
|
pa->pa_volume_ = i->volume;
|
||||||
float volume = static_cast<float>(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM};
|
float volume = static_cast<float>(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM};
|
||||||
pa->sink_idx_ = i->index;
|
pa->sink_idx_ = i->index;
|
||||||
@ -171,6 +174,10 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_
|
|||||||
*/
|
*/
|
||||||
void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i,
|
void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i,
|
||||||
void *data) {
|
void *data) {
|
||||||
|
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
||||||
|
pa->default_sink_name_ = i->default_sink_name;
|
||||||
|
pa->default_source_name_ = i->default_source_name;
|
||||||
|
|
||||||
pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data);
|
pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data);
|
||||||
pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data);
|
pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data);
|
||||||
}
|
}
|
||||||
@ -200,6 +207,7 @@ const std::string waybar::modules::Pulseaudio::getPortIcon() const {
|
|||||||
|
|
||||||
auto waybar::modules::Pulseaudio::update() -> void {
|
auto waybar::modules::Pulseaudio::update() -> void {
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
|
if (!alt_) {
|
||||||
std::string format_name = "format";
|
std::string format_name = "format";
|
||||||
if (monitor_.find("a2dp_sink") != std::string::npos) {
|
if (monitor_.find("a2dp_sink") != std::string::npos) {
|
||||||
format_name = format_name + "-bluetooth";
|
format_name = format_name + "-bluetooth";
|
||||||
@ -207,7 +215,11 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
|||||||
} else {
|
} else {
|
||||||
label_.get_style_context()->remove_class("bluetooth");
|
label_.get_style_context()->remove_class("bluetooth");
|
||||||
}
|
}
|
||||||
if (muted_ ) {
|
if (muted_) {
|
||||||
|
// Check muted bluetooth format exist, otherwise fallback to default muted format
|
||||||
|
if (format_name != "format" && !config_[format_name + "-muted"].isString()) {
|
||||||
|
format_name = "format";
|
||||||
|
}
|
||||||
format_name = format_name + "-muted";
|
format_name = format_name + "-muted";
|
||||||
label_.get_style_context()->add_class("muted");
|
label_.get_style_context()->add_class("muted");
|
||||||
} else {
|
} else {
|
||||||
@ -215,6 +227,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
|||||||
}
|
}
|
||||||
format =
|
format =
|
||||||
config_[format_name].isString() ? config_[format_name].asString() : format;
|
config_[format_name].isString() ? config_[format_name].asString() : format;
|
||||||
|
}
|
||||||
// TODO: find a better way to split source/sink
|
// TODO: find a better way to split source/sink
|
||||||
std::string format_source = "{volume}%";
|
std::string format_source = "{volume}%";
|
||||||
if (source_muted_ && config_["format-source-muted"].isString()) {
|
if (source_muted_ && config_["format-source-muted"].isString()) {
|
||||||
@ -232,4 +245,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
|||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
label_.set_tooltip_text(desc_);
|
label_.set_tooltip_text(desc_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call parent 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