mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
Compare commits
247 Commits
Author | SHA1 | Date | |
---|---|---|---|
e5787a2617 | |||
9aec6bbed4 | |||
4f6a9b1bc2 | |||
28e7a96e37 | |||
710f933fa6 | |||
bad72de960 | |||
65166109c9 | |||
91156dfc75 | |||
af2113931a | |||
68e4457f3a | |||
1b4ddbca3a | |||
445ad22580 | |||
88a5f713ed | |||
2009ceb350 | |||
77a2eff2ce | |||
cf832798fb | |||
3f3f2d9c2c | |||
b47705ac21 | |||
b33be38877 | |||
a5fe6f40b8 | |||
245f7f4b11 | |||
1418f96e46 | |||
84a8f79bbe | |||
4b6253e810 | |||
929fc16994 | |||
811f0896c9 | |||
7729ca3427 | |||
100d4d3499 | |||
7f5fd1ac86 | |||
1f5c07a07f | |||
67d482d28b | |||
1440ed29d4 | |||
311c5779ea | |||
9880c6929f | |||
99138ffdcd | |||
08e886ebc6 | |||
6fdbc27998 | |||
40e6360722 | |||
642e28166b | |||
6dfa31fb17 | |||
c91cc2218b | |||
6f2bfd43bf | |||
f43f8773c4 | |||
bb072675ba | |||
fa43072be7 | |||
86a43b9042 | |||
2506c0104a | |||
948eba92a5 | |||
ad09072a6d | |||
9c2b5efe7b | |||
91cdf80c65 | |||
9cce5ea6b5 | |||
8310700bbb | |||
78aaa5c1b4 | |||
7c1303f57c | |||
569517c531 | |||
1c2e0083ba | |||
a8edc0886d | |||
8e1f85e1c3 | |||
5420a91046 | |||
2a52efa99a | |||
d3c59c42ef | |||
368e4813de | |||
36857ae72b | |||
982d571b2e | |||
e62b634f72 | |||
e8278431d2 | |||
14f626d422 | |||
d08fbb2ef2 | |||
5da268077c | |||
20160749e7 | |||
194f4c2f18 | |||
9e34be7b16 | |||
6e041d5275 | |||
33617b67f0 | |||
efaac20d82 | |||
ce97df34e6 | |||
23b9923eeb | |||
999c1b6b81 | |||
1a98ecf6b0 | |||
5444a66e71 | |||
f49a7a1acb | |||
28dfb0ba41 | |||
94a882bf95 | |||
da2d603b53 | |||
7aaa3df701 | |||
729553d3bc | |||
f78a802d11 | |||
826a549d1f | |||
99918205ed | |||
c65ec9e14f | |||
c1427ff807 | |||
0bb436f949 | |||
0fc7ef6685 | |||
c9bbaa7241 | |||
63fdf66ad6 | |||
9357a6cb88 | |||
dbc06abf18 | |||
4d067619a8 | |||
cf3d6545c3 | |||
f3a6e2d494 | |||
cdce3e03ea | |||
b25b7d29fc | |||
71d7596b6f | |||
06e699c862 | |||
a03283d65f | |||
ef38061edd | |||
7e13e26c29 | |||
5f7329f5b9 | |||
2213380dc0 | |||
66d8035ed1 | |||
7cdf178f8d | |||
af3c868a5b | |||
b16c8972c7 | |||
1c9b62de07 | |||
fc89b01ba6 | |||
70e67c5daa | |||
5ad3b6018a | |||
ba278985e8 | |||
5300461c79 | |||
d0f60c47bf | |||
07f2470e36 | |||
f8f1e791a3 | |||
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 | |||
dc38640341 | |||
66e5fda418 | |||
e06d603154 | |||
392b0679c9 | |||
336cc9f336 | |||
0bd96f339e | |||
ce0bf6269b | |||
fdaba72974 | |||
a2d98ddde8 | |||
51bfe9eaf6 | |||
a25cf4d188 | |||
ede1146ddc | |||
b916ed3cae | |||
9d5ce45f3b | |||
a7941a00c5 | |||
f4ffb21c8c | |||
29cba22405 | |||
b79301a5bd | |||
ef9c3ef1cb | |||
1f620828c2 | |||
f20dbbbd74 | |||
00046d309d | |||
7b7edc9029 | |||
bd208fcec6 | |||
f233d27b78 | |||
42e8667773 | |||
c0361e8546 | |||
3fbbbf8541 | |||
e5684c6127 | |||
0233e0eeec | |||
7fbd3657e8 | |||
005af7f7b7 | |||
94f8f74f51 | |||
f391186749 | |||
e4340a7536 | |||
dd2792b204 | |||
08ee5385ec | |||
73eb517b86 | |||
cb7baee045 | |||
85ca5027f4 | |||
50ecc97284 | |||
d382734698 | |||
0d03c1d4da | |||
3b576ae12d | |||
96d965fe04 | |||
4229e9b2ca | |||
45f7f9b07a | |||
943b6bc51b | |||
9b51094743 | |||
8fb54f47ea |
23
.github/workflows/freebsd.yml
vendored
Normal file
23
.github/workflows/freebsd.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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.1.4 # 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 evdev-proto gtk-layer-shell gtkmm30 jsoncpp libdbusmenu \
|
||||||
|
libevdev libfmt libmpdclient libudev-devd meson pkgconf pulseaudio \
|
||||||
|
scdoc sndio spdlog
|
||||||
|
run: |
|
||||||
|
meson build -Dman-pages=enabled
|
||||||
|
ninja -C build
|
25
.github/workflows/linux.yml
vendored
Normal file
25
.github/workflows/linux.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
name: linux
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
distro:
|
||||||
|
- alpine
|
||||||
|
- archlinux
|
||||||
|
- debian
|
||||||
|
- fedora
|
||||||
|
- opensuse
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: alexays/waybar:${{ matrix.distro }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: configure
|
||||||
|
run: meson -Dman-pages=enabled build
|
||||||
|
- name: build
|
||||||
|
run: ninja -C build
|
38
.travis.yml
38
.travis.yml
@ -1,38 +0,0 @@
|
|||||||
language: cpp
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
git:
|
|
||||||
submodules: false
|
|
||||||
|
|
||||||
env:
|
|
||||||
- distro: debian
|
|
||||||
- distro: archlinux
|
|
||||||
- distro: fedora
|
|
||||||
- distro: alpine
|
|
||||||
- distro: opensuse
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- docker pull alexays/waybar:${distro}
|
|
||||||
- find . -type f \( -name '*.cpp' -o -name '*.h' \) -print0 | xargs -r0 clang-format -i
|
|
||||||
|
|
||||||
script:
|
|
||||||
- echo FROM alexays/waybar:${distro} > Dockerfile
|
|
||||||
- echo ADD . /root >> Dockerfile
|
|
||||||
- docker build -t waybar .
|
|
||||||
- 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 pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-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 libxkbcommon
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# vim: ft=Dockerfile
|
# vim: ft=Dockerfile
|
||||||
|
|
||||||
FROM archlinux/base:latest
|
FROM archlinux:base-devel
|
||||||
|
|
||||||
RUN pacman -Syu --noconfirm && \
|
RUN pacman -Syu --noconfirm && \
|
||||||
pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm
|
pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm libxkbcommon
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
FROM debian:sid
|
FROM debian:sid
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev && \
|
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 && \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
# vim: ft=Dockerfile
|
# vim: ft=Dockerfile
|
||||||
|
|
||||||
FROM fedora:32
|
FROM fedora:latest
|
||||||
|
|
||||||
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 && \
|
RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \
|
||||||
dnf group install "C Development Tools and Libraries" -y && \
|
'pkgconfig(dbusmenu-gtk3-0.4)' 'pkgconfig(fmt)' 'pkgconfig(gdk-pixbuf-2.0)' \
|
||||||
|
'pkgconfig(gio-unix-2.0)' 'pkgconfig(gtk-layer-shell-0)' 'pkgconfig(gtkmm-3.0)' \
|
||||||
|
'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \
|
||||||
|
'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \
|
||||||
|
'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \
|
||||||
|
'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' && \
|
||||||
dnf clean all -y
|
dnf clean all -y
|
||||||
|
@ -3,5 +3,7 @@
|
|||||||
FROM opensuse/tumbleweed:latest
|
FROM opensuse/tumbleweed:latest
|
||||||
|
|
||||||
RUN zypper -n up && \
|
RUN zypper -n up && \
|
||||||
|
zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \
|
||||||
|
zypper -n refresh && \
|
||||||
zypper -n install -t pattern devel_C_C++ && \
|
zypper -n install -t pattern devel_C_C++ && \
|
||||||
zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc
|
zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc
|
||||||
|
@ -68,6 +68,7 @@ libappindicator-gtk3 [Tray module]
|
|||||||
libdbusmenu-gtk3 [Tray module]
|
libdbusmenu-gtk3 [Tray module]
|
||||||
libmpdclient [MPD module]
|
libmpdclient [MPD module]
|
||||||
libsndio [sndio module]
|
libsndio [sndio module]
|
||||||
|
libevdev [KeyboardState module]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Build dependencies**
|
**Build dependencies**
|
||||||
@ -86,6 +87,7 @@ sudo apt install \
|
|||||||
clang-tidy \
|
clang-tidy \
|
||||||
gobject-introspection \
|
gobject-introspection \
|
||||||
libdbusmenu-gtk3-dev \
|
libdbusmenu-gtk3-dev \
|
||||||
|
libevdev-dev \
|
||||||
libfmt-dev \
|
libfmt-dev \
|
||||||
libgirepository1.0-dev \
|
libgirepository1.0-dev \
|
||||||
libgtk-3-dev \
|
libgtk-3-dev \
|
||||||
|
@ -14,7 +14,7 @@ class ALabel : public AModule {
|
|||||||
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);
|
virtual std::string getIcon(uint16_t, const std::vector<std::string> &alts, uint16_t max = 0);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Gtk::Label label_;
|
Gtk::Label label_;
|
||||||
|
@ -17,6 +17,7 @@ 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};
|
||||||
@ -43,6 +44,7 @@ class BarSurface {
|
|||||||
virtual void setExclusiveZone(bool enable) = 0;
|
virtual void setExclusiveZone(bool enable) = 0;
|
||||||
virtual void setLayer(bar_layer layer) = 0;
|
virtual void setLayer(bar_layer layer) = 0;
|
||||||
virtual void setMargins(const struct bar_margins &margins) = 0;
|
virtual void setMargins(const struct bar_margins &margins) = 0;
|
||||||
|
virtual void setPassThrough(bool enable) = 0;
|
||||||
virtual void setPosition(const std::string_view &position) = 0;
|
virtual void setPosition(const std::string_view &position) = 0;
|
||||||
virtual void setSize(uint32_t width, uint32_t height) = 0;
|
virtual void setSize(uint32_t width, uint32_t height) = 0;
|
||||||
virtual void commit(){};
|
virtual void commit(){};
|
||||||
@ -63,6 +65,7 @@ class Bar {
|
|||||||
struct waybar_output *output;
|
struct waybar_output *output;
|
||||||
Json::Value config;
|
Json::Value config;
|
||||||
struct wl_surface * surface;
|
struct wl_surface * surface;
|
||||||
|
bool exclusive = true;
|
||||||
bool visible = true;
|
bool visible = true;
|
||||||
bool vertical = false;
|
bool vertical = false;
|
||||||
Gtk::Window window;
|
Gtk::Window window;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#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 zwlr_layer_shell_v1;
|
||||||
@ -18,6 +19,7 @@ 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;
|
||||||
@ -33,20 +35,25 @@ class Client {
|
|||||||
std::tuple<const std::string, const std::string> getConfigs(const std::string &config,
|
std::tuple<const std::string, const std::string> getConfigs(const std::string &config,
|
||||||
const std::string &style) const;
|
const std::string &style) const;
|
||||||
void bindInterfaces();
|
void bindInterfaces();
|
||||||
const std::string getValidPath(const std::vector<std::string> &paths) const;
|
const std::string getValidPath(const std::vector<std::string> &paths) const;
|
||||||
void handleOutput(struct waybar_output &output);
|
void handleOutput(struct waybar_output &output);
|
||||||
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, int depth) -> void;
|
||||||
auto setupCss(const std::string &css_file) -> void;
|
auto resolveConfigIncludes(Json::Value &config, int depth) -> void;
|
||||||
struct waybar_output &getOutput(void *);
|
auto mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void;
|
||||||
|
auto setupCss(const std::string &css_file) -> 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,7 +1,11 @@
|
|||||||
#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"
|
||||||
@ -34,6 +38,9 @@
|
|||||||
#ifdef HAVE_LIBUDEV
|
#ifdef HAVE_LIBUDEV
|
||||||
#include "modules/backlight.hpp"
|
#include "modules/backlight.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_LIBEVDEV
|
||||||
|
#include "modules/keyboard_state.hpp"
|
||||||
|
#endif
|
||||||
#ifdef HAVE_LIBPULSE
|
#ifdef HAVE_LIBPULSE
|
||||||
#include "modules/pulseaudio.hpp"
|
#include "modules/pulseaudio.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
@ -31,11 +31,11 @@ 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 refreshBatteries();
|
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 std::tuple<uint8_t, float, std::string, float> getInfos();
|
||||||
const std::string formatTimeRemaining(float hoursRemaining);
|
const std::string formatTimeRemaining(float hoursRemaining);
|
||||||
|
|
||||||
int global_watch;
|
int global_watch;
|
||||||
std::map<fs::path,int> batteries_;
|
std::map<fs::path,int> batteries_;
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
|
|
||||||
#include <fmt/chrono.h>
|
|
||||||
#include "util/sleeper_thread.hpp"
|
|
||||||
#include "util/rfkill.hpp"
|
#include "util/rfkill.hpp"
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
@ -16,10 +12,6 @@ class Bluetooth : public ALabel {
|
|||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string status_;
|
|
||||||
util::SleeperThread thread_;
|
|
||||||
util::SleeperThread intervall_thread_;
|
|
||||||
|
|
||||||
util::Rfkill rfkill_;
|
util::Rfkill rfkill_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
@ -20,9 +19,11 @@ class Cpu : public ALabel {
|
|||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint16_t getCpuLoad();
|
double 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_;
|
||||||
|
|
||||||
|
47
include/modules/keyboard_state.hpp
Normal file
47
include/modules/keyboard_state.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#if FMT_VERSION < 60000
|
||||||
|
#include <fmt/time.h>
|
||||||
|
#else
|
||||||
|
#include <fmt/chrono.h>
|
||||||
|
#endif
|
||||||
|
#include "AModule.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "util/sleeper_thread.hpp"
|
||||||
|
#include <gtkmm/label.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libevdev/libevdev.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class KeyboardState : public AModule {
|
||||||
|
public:
|
||||||
|
KeyboardState(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
|
~KeyboardState();
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static auto openDevice(const std::string&) -> std::pair<int, libevdev*>;
|
||||||
|
|
||||||
|
Gtk::Box box_;
|
||||||
|
Gtk::Label numlock_label_;
|
||||||
|
Gtk::Label capslock_label_;
|
||||||
|
Gtk::Label scrolllock_label_;
|
||||||
|
|
||||||
|
std::string numlock_format_;
|
||||||
|
std::string capslock_format_;
|
||||||
|
std::string scrolllock_format_;
|
||||||
|
const std::chrono::seconds interval_;
|
||||||
|
std::string icon_locked_;
|
||||||
|
std::string icon_unlocked_;
|
||||||
|
|
||||||
|
int fd_;
|
||||||
|
libevdev* dev_;
|
||||||
|
|
||||||
|
util::SleeperThread thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <linux/nl80211.h>
|
#include <linux/nl80211.h>
|
||||||
#include <net/if.h>
|
|
||||||
#include <netlink/genl/ctrl.h>
|
#include <netlink/genl/ctrl.h>
|
||||||
#include <netlink/genl/genl.h>
|
#include <netlink/genl/genl.h>
|
||||||
#include <netlink/netlink.h>
|
#include <netlink/netlink.h>
|
||||||
@ -28,23 +26,20 @@ class Network : public ALabel {
|
|||||||
static const uint8_t EPOLL_MAX = 200;
|
static const uint8_t EPOLL_MAX = 200;
|
||||||
|
|
||||||
static int handleEvents(struct nl_msg*, void*);
|
static int handleEvents(struct nl_msg*, void*);
|
||||||
|
static int handleEventsDone(struct nl_msg*, void*);
|
||||||
static int handleScan(struct nl_msg*, void*);
|
static int handleScan(struct nl_msg*, void*);
|
||||||
|
|
||||||
|
void askForStateDump(void);
|
||||||
|
|
||||||
void worker();
|
void worker();
|
||||||
void createInfoSocket();
|
void createInfoSocket();
|
||||||
void createEventSocket();
|
void createEventSocket();
|
||||||
int getExternalInterface(int skip_idx = -1) const;
|
|
||||||
void getInterfaceAddress();
|
|
||||||
int netlinkRequest(void*, uint32_t, uint32_t groups = 0) const;
|
|
||||||
int netlinkResponse(void*, uint32_t, uint32_t groups = 0) const;
|
|
||||||
void parseEssid(struct nlattr**);
|
void parseEssid(struct nlattr**);
|
||||||
void parseSignal(struct nlattr**);
|
void parseSignal(struct nlattr**);
|
||||||
void parseFreq(struct nlattr**);
|
void parseFreq(struct nlattr**);
|
||||||
bool associatedOrJoined(struct nlattr**);
|
bool associatedOrJoined(struct nlattr**);
|
||||||
bool checkInterface(struct ifinfomsg* rtif, std::string name);
|
bool checkInterface(std::string name);
|
||||||
int getPreferredIface(int skip_idx = -1, bool wait = true) const;
|
|
||||||
auto getInfo() -> void;
|
auto getInfo() -> void;
|
||||||
void checkNewInterface(struct ifinfomsg* rtif);
|
|
||||||
const std::string getNetworkState() const;
|
const std::string getNetworkState() const;
|
||||||
void clearIface();
|
void clearIface();
|
||||||
bool wildcardMatch(const std::string& pattern, const std::string& text) const;
|
bool wildcardMatch(const std::string& pattern, const std::string& text) const;
|
||||||
@ -59,11 +54,17 @@ class Network : public ALabel {
|
|||||||
int nl80211_id_;
|
int nl80211_id_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
|
|
||||||
|
bool want_route_dump_;
|
||||||
|
bool want_link_dump_;
|
||||||
|
bool want_addr_dump_;
|
||||||
|
bool dump_in_progress_;
|
||||||
|
|
||||||
unsigned long long bandwidth_down_total_;
|
unsigned long long bandwidth_down_total_;
|
||||||
unsigned long long bandwidth_up_total_;
|
unsigned long long bandwidth_up_total_;
|
||||||
|
|
||||||
std::string state_;
|
std::string state_;
|
||||||
std::string essid_;
|
std::string essid_;
|
||||||
|
bool carrier_;
|
||||||
std::string ifname_;
|
std::string ifname_;
|
||||||
std::string ipaddr_;
|
std::string ipaddr_;
|
||||||
std::string netmask_;
|
std::string netmask_;
|
||||||
@ -71,12 +72,11 @@ class Network : public ALabel {
|
|||||||
int32_t signal_strength_dbm_;
|
int32_t signal_strength_dbm_;
|
||||||
uint8_t signal_strength_;
|
uint8_t signal_strength_;
|
||||||
uint32_t frequency_;
|
uint32_t frequency_;
|
||||||
|
uint32_t route_priority;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
util::SleeperThread thread_timer_;
|
util::SleeperThread thread_timer_;
|
||||||
#ifdef WANT_RFKILL
|
#ifdef WANT_RFKILL
|
||||||
util::SleeperThread thread_rfkill_;
|
|
||||||
|
|
||||||
util::Rfkill rfkill_;
|
util::Rfkill rfkill_;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -24,7 +24,7 @@ class Pulseaudio : public ALabel {
|
|||||||
static void volumeModifyCb(pa_context*, int, void*);
|
static void volumeModifyCb(pa_context*, int, void*);
|
||||||
|
|
||||||
bool handleScroll(GdkEventScroll* e);
|
bool handleScroll(GdkEventScroll* e);
|
||||||
const std::string getPortIcon() const;
|
const std::vector<std::string> getPulseIcon() const;
|
||||||
|
|
||||||
pa_threaded_mainloop* mainloop_;
|
pa_threaded_mainloop* mainloop_;
|
||||||
pa_mainloop_api* mainloop_api_;
|
pa_mainloop_api* mainloop_api_;
|
||||||
@ -38,7 +38,8 @@ 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_;
|
std::string current_sink_name_;
|
||||||
|
bool current_sink_running_;
|
||||||
// SOURCE
|
// SOURCE
|
||||||
uint32_t source_idx_{0};
|
uint32_t source_idx_{0};
|
||||||
uint16_t source_volume_;
|
uint16_t source_volume_;
|
||||||
|
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
|
@ -11,8 +11,16 @@
|
|||||||
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
|
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
|
||||||
#include <sigc++/trackable.h>
|
#include <sigc++/trackable.h>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
namespace waybar::modules::SNI {
|
namespace waybar::modules::SNI {
|
||||||
|
|
||||||
|
struct ToolTip {
|
||||||
|
Glib::ustring icon_name;
|
||||||
|
Glib::ustring text;
|
||||||
|
};
|
||||||
|
|
||||||
class Item : public sigc::trackable {
|
class Item : public sigc::trackable {
|
||||||
public:
|
public:
|
||||||
Item(const std::string&, const std::string&, const Json::Value&);
|
Item(const std::string&, const std::string&, const Json::Value&);
|
||||||
@ -27,10 +35,8 @@ class Item : public sigc::trackable {
|
|||||||
Gtk::EventBox event_box;
|
Gtk::EventBox event_box;
|
||||||
std::string category;
|
std::string category;
|
||||||
std::string id;
|
std::string id;
|
||||||
std::string status;
|
|
||||||
|
|
||||||
std::string title;
|
std::string title;
|
||||||
int32_t window_id;
|
|
||||||
std::string icon_name;
|
std::string icon_name;
|
||||||
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
||||||
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
||||||
@ -39,6 +45,7 @@ class Item : public sigc::trackable {
|
|||||||
std::string attention_movie_name;
|
std::string attention_movie_name;
|
||||||
std::string icon_theme_path;
|
std::string icon_theme_path;
|
||||||
std::string menu;
|
std::string menu;
|
||||||
|
ToolTip tooltip;
|
||||||
DbusmenuGtkMenu* dbus_menu = nullptr;
|
DbusmenuGtkMenu* dbus_menu = nullptr;
|
||||||
Gtk::Menu* gtk_menu = nullptr;
|
Gtk::Menu* gtk_menu = nullptr;
|
||||||
/**
|
/**
|
||||||
@ -51,6 +58,7 @@ class Item : public sigc::trackable {
|
|||||||
private:
|
private:
|
||||||
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
|
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||||
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
|
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
|
||||||
|
void setStatus(const Glib::ustring& value);
|
||||||
void getUpdatedProperties();
|
void getUpdatedProperties();
|
||||||
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
|
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||||
void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
||||||
@ -58,14 +66,24 @@ class Item : public sigc::trackable {
|
|||||||
|
|
||||||
void updateImage();
|
void updateImage();
|
||||||
Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
|
Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> getIconPixbuf();
|
||||||
Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
|
Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
|
||||||
|
double getScaledIconSize();
|
||||||
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
|
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
|
||||||
void makeMenu();
|
void makeMenu();
|
||||||
bool handleClick(GdkEventButton* const& /*ev*/);
|
bool handleClick(GdkEventButton* const& /*ev*/);
|
||||||
|
bool handleScroll(GdkEventScroll* const&);
|
||||||
|
|
||||||
|
// smooth scrolling threshold
|
||||||
|
gdouble scroll_threshold_ = 0;
|
||||||
|
gdouble distance_scrolled_x_ = 0;
|
||||||
|
gdouble distance_scrolled_y_ = 0;
|
||||||
|
// visibility of items with Status == Passive
|
||||||
|
bool show_passive_ = false;
|
||||||
|
|
||||||
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
|
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
|
||||||
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
||||||
bool update_pending_;
|
std::set<std::string_view> update_pending_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::SNI
|
} // namespace waybar::modules::SNI
|
||||||
|
@ -29,4 +29,8 @@ enum ipc_command_type {
|
|||||||
IPC_EVENT_BINDING = ((1 << 31) | 5),
|
IPC_EVENT_BINDING = ((1 << 31) | 5),
|
||||||
IPC_EVENT_SHUTDOWN = ((1 << 31) | 6),
|
IPC_EVENT_SHUTDOWN = ((1 << 31) | 6),
|
||||||
IPC_EVENT_TICK = ((1 << 31) | 7),
|
IPC_EVENT_TICK = ((1 << 31) | 7),
|
||||||
|
|
||||||
|
// sway-specific event types
|
||||||
|
IPC_EVENT_BAR_STATE_UPDATE = ((1<<31) | 20),
|
||||||
|
IPC_EVENT_INPUT = ((1<<31) | 21),
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include <xkbcommon/xkbregistry.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
@ -16,13 +21,41 @@ class Language : public ALabel, public sigc::trackable {
|
|||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Layout {
|
||||||
|
std::string full_name;
|
||||||
|
std::string short_name;
|
||||||
|
std::string variant;
|
||||||
|
};
|
||||||
|
|
||||||
|
class XKBContext {
|
||||||
|
public:
|
||||||
|
XKBContext();
|
||||||
|
~XKBContext();
|
||||||
|
auto next_layout() -> Layout*;
|
||||||
|
private:
|
||||||
|
rxkb_context* context_ = nullptr;
|
||||||
|
rxkb_layout* xkb_layout_ = nullptr;
|
||||||
|
Layout* layout_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
void onEvent(const struct Ipc::ipc_response&);
|
void onEvent(const struct Ipc::ipc_response&);
|
||||||
void onCmd(const struct Ipc::ipc_response&);
|
void onCmd(const struct Ipc::ipc_response&);
|
||||||
|
|
||||||
|
auto set_current_layout(std::string current_layout) -> void;
|
||||||
|
auto init_layouts_map(const std::vector<std::string>& used_layouts) -> void;
|
||||||
|
|
||||||
|
const static std::string XKB_LAYOUT_NAMES_KEY;
|
||||||
|
const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY;
|
||||||
|
|
||||||
std::string lang_;
|
Layout layout_;
|
||||||
util::JsonParser parser_;
|
std::string tooltip_format_ = "";
|
||||||
std::mutex mutex_;
|
std::map<std::string, Layout> layouts_map_;
|
||||||
Ipc ipc_;
|
XKBContext xkb_context_;
|
||||||
|
bool is_variant_displayed;
|
||||||
|
|
||||||
|
util::JsonParser parser_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
Ipc ipc_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::sway
|
} // namespace waybar::modules::sway
|
||||||
|
@ -22,6 +22,7 @@ class Window : public ALabel, public sigc::trackable {
|
|||||||
std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes,
|
std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes,
|
||||||
std::string& output);
|
std::string& output);
|
||||||
void getTree();
|
void getTree();
|
||||||
|
std::string rewriteTitle(const std::string& title);
|
||||||
|
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
std::string window_;
|
std::string window_;
|
||||||
|
@ -20,7 +20,7 @@ class Workspaces : public AModule, public sigc::trackable {
|
|||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static inline const std::string workspace_switch_cmd_ = "workspace --no-auto-back-and-forth \"{}\"";
|
static inline const std::string workspace_switch_cmd_ = "workspace {} \"{}\"";
|
||||||
|
|
||||||
static int convertWorkspaceNameToNum(std::string name);
|
static int convertWorkspaceNameToNum(std::string name);
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <gdk/gdk.h>
|
#include <gdk/gdk.h>
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ class Task
|
|||||||
Gtk::Label text_before_;
|
Gtk::Label text_before_;
|
||||||
Gtk::Label text_after_;
|
Gtk::Label text_after_;
|
||||||
bool button_visible_;
|
bool button_visible_;
|
||||||
|
bool ignored_;
|
||||||
|
|
||||||
bool with_icon_;
|
bool with_icon_;
|
||||||
std::string format_before_;
|
std::string format_before_;
|
||||||
@ -132,6 +134,7 @@ class Taskbar : public waybar::AModule
|
|||||||
std::vector<TaskPtr> tasks_;
|
std::vector<TaskPtr> tasks_;
|
||||||
|
|
||||||
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes_;
|
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes_;
|
||||||
|
std::unordered_set<std::string> ignore_list_;
|
||||||
|
|
||||||
struct zwlr_foreign_toplevel_manager_v1 *manager_;
|
struct zwlr_foreign_toplevel_manager_v1 *manager_;
|
||||||
struct wl_seat *seat_;
|
struct wl_seat *seat_;
|
||||||
@ -155,6 +158,7 @@ class Taskbar : public waybar::AModule
|
|||||||
bool all_outputs() const;
|
bool all_outputs() const;
|
||||||
|
|
||||||
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes() const;
|
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes() const;
|
||||||
|
const std::unordered_set<std::string>& ignore_list() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace waybar::modules::wlr */
|
} /* namespace waybar::modules::wlr */
|
||||||
|
@ -5,6 +5,13 @@
|
|||||||
#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>
|
#include <array>
|
||||||
|
|
||||||
extern std::mutex reap_mtx;
|
extern std::mutex reap_mtx;
|
||||||
@ -77,6 +84,18 @@ inline FILE* open(const std::string& cmd, int& pid) {
|
|||||||
// Reset sigmask
|
// Reset sigmask
|
||||||
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
|
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
|
||||||
if (err != 0) spdlog::error("pthread_sigmask in open failed: {}", strerror(err));
|
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);
|
||||||
|
@ -35,14 +35,18 @@ namespace fmt {
|
|||||||
// The rationale for ignoring it is that the only reason to specify
|
// The rationale for ignoring it is that the only reason to specify
|
||||||
// an alignment and a with is to get a fixed width bar, and ">" is
|
// an alignment and a with is to get a fixed width bar, and ">" is
|
||||||
// sufficient in this implementation.
|
// sufficient in this implementation.
|
||||||
|
#if FMT_VERSION < 80000
|
||||||
width = parse_nonnegative_int(it, end, ctx);
|
width = parse_nonnegative_int(it, end, ctx);
|
||||||
|
#else
|
||||||
|
width = detail::parse_nonnegative_int(it, end, -1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class FormatContext>
|
template<class FormatContext>
|
||||||
auto format(const pow_format& s, FormatContext &ctx) -> decltype (ctx.out()) {
|
auto format(const pow_format& s, FormatContext &ctx) -> decltype (ctx.out()) {
|
||||||
const char* units[] = { "", "K", "M", "G", "T", "P", nullptr};
|
const char* units[] = { "", "k", "M", "G", "T", "P", nullptr};
|
||||||
|
|
||||||
auto base = s.binary_ ? 1024ull : 1000ll;
|
auto base = s.binary_ ? 1024ull : 1000ll;
|
||||||
auto fraction = (double) s.val_;
|
auto fraction = (double) s.val_;
|
||||||
|
@ -1,19 +1,26 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <glibmm/iochannel.h>
|
||||||
#include <linux/rfkill.h>
|
#include <linux/rfkill.h>
|
||||||
|
#include <sigc++/signal.h>
|
||||||
|
#include <sigc++/trackable.h>
|
||||||
|
|
||||||
namespace waybar::util {
|
namespace waybar::util {
|
||||||
|
|
||||||
class Rfkill {
|
class Rfkill : public sigc::trackable {
|
||||||
public:
|
public:
|
||||||
Rfkill(enum rfkill_type rfkill_type);
|
Rfkill(enum rfkill_type rfkill_type);
|
||||||
~Rfkill() = default;
|
~Rfkill();
|
||||||
void waitForEvent();
|
|
||||||
bool getState() const;
|
bool getState() const;
|
||||||
|
|
||||||
|
sigc::signal<void(struct rfkill_event&)> on_update;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum rfkill_type rfkill_type_;
|
enum rfkill_type rfkill_type_;
|
||||||
int state_ = 0;
|
bool state_ = false;
|
||||||
|
int fd_ = -1;
|
||||||
|
|
||||||
|
bool on_event(Glib::IOCondition cond);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::util
|
} // namespace waybar::util
|
||||||
|
@ -8,6 +8,20 @@
|
|||||||
|
|
||||||
namespace waybar::util {
|
namespace waybar::util {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defer pthread_cancel until the end of a current scope.
|
||||||
|
*
|
||||||
|
* Required to protect a scope where it's unsafe to raise `__forced_unwind` exception.
|
||||||
|
* An example of these is a call of a method marked as `noexcept`; an attempt to cancel within such
|
||||||
|
* a method may result in a `std::terminate` call.
|
||||||
|
*/
|
||||||
|
class CancellationGuard {
|
||||||
|
int oldstate;
|
||||||
|
public:
|
||||||
|
CancellationGuard() { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); }
|
||||||
|
~CancellationGuard() { pthread_setcancelstate(oldstate, &oldstate); }
|
||||||
|
};
|
||||||
|
|
||||||
class SleeperThread {
|
class SleeperThread {
|
||||||
public:
|
public:
|
||||||
SleeperThread() = default;
|
SleeperThread() = default;
|
||||||
@ -33,14 +47,16 @@ class SleeperThread {
|
|||||||
bool isRunning() const { return do_run_; }
|
bool isRunning() const { return do_run_; }
|
||||||
|
|
||||||
auto sleep_for(std::chrono::system_clock::duration dur) {
|
auto sleep_for(std::chrono::system_clock::duration dur) {
|
||||||
std::unique_lock lk(mutex_);
|
std::unique_lock lk(mutex_);
|
||||||
|
CancellationGuard cancel_lock;
|
||||||
return condvar_.wait_for(lk, dur, [this] { return signal_ || !do_run_; });
|
return condvar_.wait_for(lk, dur, [this] { return signal_ || !do_run_; });
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sleep_until(
|
auto sleep_until(
|
||||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::system_clock::duration>
|
std::chrono::time_point<std::chrono::system_clock, std::chrono::system_clock::duration>
|
||||||
time_point) {
|
time_point) {
|
||||||
std::unique_lock lk(mutex_);
|
std::unique_lock lk(mutex_);
|
||||||
|
CancellationGuard cancel_lock;
|
||||||
return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; });
|
return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
include/util/string.hpp
Normal file
15
include/util/string.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include <string>
|
||||||
|
|
||||||
|
const std::string WHITESPACE = " \n\r\t\f\v";
|
||||||
|
|
||||||
|
std::string ltrim(const std::string s) {
|
||||||
|
size_t begin = s.find_first_not_of(WHITESPACE);
|
||||||
|
return (begin == std::string::npos) ? "" : s.substr(begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rtrim(const std::string s) {
|
||||||
|
size_t end = s.find_last_not_of(WHITESPACE);
|
||||||
|
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string trim(const std::string& s) { return rtrim(ltrim(s)); }
|
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);
|
@ -24,6 +24,14 @@ The *backlight* module displays the current backlight level.
|
|||||||
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.
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
|
@ -22,6 +22,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
typeof: integer ++
|
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%.
|
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*: ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
default: 60 ++
|
default: 60 ++
|
||||||
@ -50,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.
|
||||||
@ -91,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.
|
||||||
|
@ -12,11 +12,6 @@ The *bluetooth* module displays information about the status of the device's blu
|
|||||||
|
|
||||||
Addressed by *bluetooth*
|
Addressed by *bluetooth*
|
||||||
|
|
||||||
*interval*: ++
|
|
||||||
typeof: integer ++
|
|
||||||
default: 60 ++
|
|
||||||
The interval in which the bluetooth state gets updated.
|
|
||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: *{icon}* ++
|
default: *{icon}* ++
|
||||||
@ -35,6 +30,14 @@ Addressed by *bluetooth*
|
|||||||
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.
|
||||||
@ -80,12 +83,11 @@ Addressed by *bluetooth*
|
|||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
"format": "{icon}",
|
"format": "{icon}",
|
||||||
"format-alt": "bluetooth: {status}",
|
"format-alt": "bluetooth: {status}",
|
||||||
"interval": 30,
|
|
||||||
"format-icons": {
|
"format-icons": {
|
||||||
"enabled": "",
|
"enabled": "",
|
||||||
"disabled": ""
|
"disabled": ""
|
||||||
},
|
},
|
||||||
"tooltip-format": "{status}"
|
"tooltip-format": "{}"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -45,6 +45,14 @@ The *clock* module displays the current date and time.
|
|||||||
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.
|
||||||
|
@ -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.
|
||||||
@ -71,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
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -67,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.
|
||||||
|
@ -39,6 +39,14 @@ Addressed by *disk*
|
|||||||
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.
|
||||||
|
@ -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
|
||||||
|
80
man/waybar-keyboard-state.5.scd
Normal file
80
man/waybar-keyboard-state.5.scd
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
waybar-keyboard-state(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - keyboard-state module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *keyboard-state* module displays the state of number lock, caps lock, and scroll lock.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
*interval*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
default: 1 ++
|
||||||
|
The interval, in seconds, to poll the keyboard state.
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string|object ++
|
||||||
|
default: {name} {icon} ++
|
||||||
|
The format, how information should be displayed. If a string, the same format is used for all keyboard states. If an object, the fields "numlock", "capslock", and "scrolllock" each specify the format for the corresponding state. Any unspecified states use the default format.
|
||||||
|
|
||||||
|
*format-icons*: ++
|
||||||
|
typeof: object ++
|
||||||
|
default: {"locked": "locked", "unlocked": "unlocked"} ++
|
||||||
|
Based on the keyboard state, the corresponding icon gets selected. The same set of icons is used for number, caps, and scroll lock, but the icon is selected from the set independently for each. See *icons*.
|
||||||
|
|
||||||
|
*numlock*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Display the number lock state.
|
||||||
|
|
||||||
|
*capslock*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Display the caps lock state.
|
||||||
|
|
||||||
|
*scrolllock*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Display the scroll lock state.
|
||||||
|
|
||||||
|
*device-path*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: chooses first valid input device ++
|
||||||
|
Which libevdev input device to show the state of. Libevdev devices can be found in /dev/input. The device should support number lock, caps lock, and scroll lock events.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{name}*: Caps, Num, or Scroll.
|
||||||
|
|
||||||
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
|
# ICONS
|
||||||
|
|
||||||
|
The following *format-icons* can be set.
|
||||||
|
|
||||||
|
- *locked*: Will be shown when the keyboard state is locked. Default "locked".
|
||||||
|
- *unlocked*: Will be shown when the keyboard state is not locked. Default "unlocked"
|
||||||
|
|
||||||
|
# EXAMPLE:
|
||||||
|
|
||||||
|
```
|
||||||
|
"keyboard-state": {
|
||||||
|
"numlock": true,
|
||||||
|
"capslock": true,
|
||||||
|
"format": "{name} {icon}",
|
||||||
|
"format-icons": {
|
||||||
|
"locked": "",
|
||||||
|
"unlocked": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#keyboard-state*
|
||||||
|
- *#keyboard-state label*
|
||||||
|
- *#keyboard-state label.locked*
|
||||||
|
|
@ -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.
|
||||||
|
@ -73,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.
|
||||||
@ -81,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.
|
||||||
@ -148,6 +172,8 @@ Addressed by *mpd*
|
|||||||
|
|
||||||
*{date}*: The date of the current song
|
*{date}*: The date of the current song
|
||||||
|
|
||||||
|
*{volume}*: The current volume in percent
|
||||||
|
|
||||||
*{elapsedTime}*: The current position of the current song. To format as a date/time (see example configuration)
|
*{elapsedTime}*: The current position 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)
|
*{totalTime}*: The length of the current song. To format as a date/time (see example configuration)
|
||||||
|
@ -64,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.
|
||||||
|
@ -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 ++
|
||||||
@ -101,6 +109,9 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu
|
|||||||
# ICONS:
|
# ICONS:
|
||||||
|
|
||||||
The following strings for *format-icons* are supported.
|
The following strings for *format-icons* are supported.
|
||||||
|
|
||||||
|
- the device name
|
||||||
|
|
||||||
If they are found in the current PulseAudio port name, the corresponding icons will be selected.
|
If they are found in the current PulseAudio port name, the corresponding icons will be selected.
|
||||||
|
|
||||||
- *default* (Shown, when no other port is found)
|
- *default* (Shown, when no other port is found)
|
||||||
@ -123,6 +134,7 @@ If they are found in the current PulseAudio port name, the corresponding icons w
|
|||||||
"format-bluetooth": "{volume}% {icon}",
|
"format-bluetooth": "{volume}% {icon}",
|
||||||
"format-muted": "",
|
"format-muted": "",
|
||||||
"format-icons": {
|
"format-icons": {
|
||||||
|
"alsa_output.pci-0000_00_1f.3.analog-stereo": "",
|
||||||
"headphones": "",
|
"headphones": "",
|
||||||
"handsfree": "",
|
"handsfree": "",
|
||||||
"headset": "",
|
"headset": "",
|
||||||
|
@ -17,6 +17,10 @@ Addressed by *river/tags*
|
|||||||
default: 9 ++
|
default: 9 ++
|
||||||
The number of tags that should be displayed.
|
The number of tags that should be displayed.
|
||||||
|
|
||||||
|
*tag-labels*: ++
|
||||||
|
typeof: array ++
|
||||||
|
The label to display for each tag.
|
||||||
|
|
||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -26,6 +26,14 @@ cursor is over the module, and clicking on the module toggles mute.
|
|||||||
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: int ++
|
typeof: int ++
|
||||||
default: 5 ++
|
default: 5 ++
|
||||||
|
@ -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
|
||||||
|
@ -15,55 +15,35 @@ Addressed by *sway/language*
|
|||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {} ++
|
default: {} ++
|
||||||
The format, how information should be displayed. On {} data gets inserted.
|
The format, how layout should be displayed.
|
||||||
|
|
||||||
*rotate*: ++
|
*tooltip-format*: ++
|
||||||
typeof: integer ++
|
|
||||||
Positive value to rotate the text label.
|
|
||||||
|
|
||||||
*max-length*: ++
|
|
||||||
typeof: integer ++
|
|
||||||
The maximum length in character the module should display.
|
|
||||||
|
|
||||||
*on-click*: ++
|
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when clicked on the module.
|
default: {} ++
|
||||||
|
The format, how layout should be displayed in tooltip.
|
||||||
*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*: ++
|
*tooltip*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: true ++
|
||||||
Option to disable tooltip on hover.
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{short}*: Short name of layout (e.g. "en"). Equals to {}.
|
||||||
|
|
||||||
|
*{long}*: Long name of layout (e.g. "English (Dvorak)").
|
||||||
|
|
||||||
|
*{variant}*: Variant of layout (e.g. "Dvorak").
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
"sway/language": {
|
"sway/language": {
|
||||||
"format": "{}",
|
"format": "{}",
|
||||||
"max-length": 50
|
},
|
||||||
|
|
||||||
|
"sway/language": {
|
||||||
|
"format": "{short} {variant}",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
@ -58,12 +66,32 @@ Addressed by *sway/window*
|
|||||||
default: true ++
|
default: true ++
|
||||||
Option to disable tooltip on hover.
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
*rewrite*: ++
|
||||||
|
typeof: object ++
|
||||||
|
Rules to rewrite window title. See *rewrite rules*.
|
||||||
|
|
||||||
|
# REWRITE RULES
|
||||||
|
|
||||||
|
*rewrite* is an object where keys are regular expressions and values are
|
||||||
|
rewrite rules if the expression matches. Rules may contain references to
|
||||||
|
captures of the expression.
|
||||||
|
|
||||||
|
Regular expression and replacement follow ECMA-script rules.
|
||||||
|
|
||||||
|
If no expression matches, the title is left unchanged.
|
||||||
|
|
||||||
|
Invalid expressions (e.g., mismatched parentheses) are skipped.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
"sway/window": {
|
"sway/window": {
|
||||||
"format": "{}",
|
"format": "{}",
|
||||||
"max-length": 50
|
"max-length": 50,
|
||||||
|
"rewrite": {
|
||||||
|
"(.*) - Mozilla Firefox": "🌎 $1",
|
||||||
|
"(.*) - zsh": "> [$1]"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -66,12 +66,16 @@ Addressed by *sway/workspaces*
|
|||||||
Lists workspaces that should always be shown, even when non existent
|
Lists workspaces that should always be shown, even when non existent
|
||||||
|
|
||||||
*on-update*: ++
|
*on-update*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when the module is updated.
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
*numeric-first*: ++
|
*numeric-first*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
Whether to put workspaces starting with numbers before workspaces that do not start with a number.
|
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
|
||||||
|
|
||||||
|
@ -50,6 +50,11 @@ Addressed by *temperature*
|
|||||||
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.
|
||||||
@ -58,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.
|
||||||
|
@ -16,6 +16,15 @@ Addressed by *tray*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Defines the size of the tray icons.
|
Defines the size of the tray icons.
|
||||||
|
|
||||||
|
*show-passive-items*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Defines visibility of the tray icons with *Passive* status.
|
||||||
|
|
||||||
|
*smooth-scrolling-threshold*: ++
|
||||||
|
typeof: double ++
|
||||||
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
*spacing*: ++
|
*spacing*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Defines the spacing between the tray icons.
|
Defines the spacing between the tray icons.
|
||||||
@ -37,3 +46,6 @@ Addressed by *tray*
|
|||||||
# STYLE
|
# STYLE
|
||||||
|
|
||||||
- *#tray*
|
- *#tray*
|
||||||
|
- *#tray > .passive*
|
||||||
|
- *#tray > .active*
|
||||||
|
- *#tray > .needs-attention*
|
||||||
|
@ -68,6 +68,10 @@ Addressed by *wlr/taskbar*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when the module is updated.
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
*ignore-list*: ++
|
||||||
|
typeof: array ++
|
||||||
|
List of app_id to be invisible.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{icon}*: The icon of the application.
|
*{icon}*: The icon of the application.
|
||||||
@ -98,7 +102,10 @@ Addressed by *wlr/taskbar*
|
|||||||
"icon-theme": "Numix-Circle",
|
"icon-theme": "Numix-Circle",
|
||||||
"tooltip-format": "{title}",
|
"tooltip-format": "{title}",
|
||||||
"on-click": "activate",
|
"on-click": "activate",
|
||||||
"on-click-middle": "close"
|
"on-click-middle": "close",
|
||||||
|
"ignore-list": [
|
||||||
|
"Alacritty"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -68,12 +68,28 @@ Also a minimal example configuration can be found on the at the bottom of this m
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Optional name added as a CSS class, for styling multiple waybars.
|
Optional name added as a CSS class, for styling multiple waybars.
|
||||||
|
|
||||||
|
*exclusive* ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: *true* unless the layer is set to *overlay* ++
|
||||||
|
Option to request an exclusive zone from the compositor. Disable this to allow drawing application windows underneath or on top of the bar.
|
||||||
|
|
||||||
|
*passthrough* ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: *false* unless the layer is set to *overlay* ++
|
||||||
|
Option to pass any pointer events to the window under the bar.
|
||||||
|
Intended to be used with either *top* or *overlay* layers and without exclusive zone.
|
||||||
|
|
||||||
*gtk-layer-shell* ++
|
*gtk-layer-shell* ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: true ++
|
||||||
Option to disable the use of gtk-layer-shell for popups.
|
Option to disable the use of gtk-layer-shell for popups.
|
||||||
Only functional if compiled with gtk-layer-shell support.
|
Only functional if compiled with gtk-layer-shell support.
|
||||||
|
|
||||||
|
*include* ++
|
||||||
|
typeof: string|array ++
|
||||||
|
Paths to additional configuration files. In case of duplicate options, the including file's value takes precedence. Make sure to avoid circular imports.
|
||||||
|
For a multi-bar config, specify at least an empty object for each bar also in every file being included.
|
||||||
|
|
||||||
# MODULE FORMAT
|
# MODULE FORMAT
|
||||||
|
|
||||||
You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat).
|
You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat).
|
||||||
@ -192,6 +208,7 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270.
|
|||||||
- *waybar-custom(5)*
|
- *waybar-custom(5)*
|
||||||
- *waybar-disk(5)*
|
- *waybar-disk(5)*
|
||||||
- *waybar-idle-inhibitor(5)*
|
- *waybar-idle-inhibitor(5)*
|
||||||
|
- *waybar-keyboard-state(5)*
|
||||||
- *waybar-memory(5)*
|
- *waybar-memory(5)*
|
||||||
- *waybar-mpd(5)*
|
- *waybar-mpd(5)*
|
||||||
- *waybar-network(5)*
|
- *waybar-network(5)*
|
||||||
|
32
meson.build
32
meson.build
@ -1,6 +1,6 @@
|
|||||||
project(
|
project(
|
||||||
'waybar', 'cpp', 'c',
|
'waybar', 'cpp', 'c',
|
||||||
version: '0.9.5',
|
version: '0.9.8',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>= 0.49.0',
|
meson_version: '>= 0.49.0',
|
||||||
default_options : [
|
default_options : [
|
||||||
@ -80,7 +80,7 @@ is_openbsd = host_machine.system() == 'openbsd'
|
|||||||
|
|
||||||
thread_dep = dependency('threads')
|
thread_dep = dependency('threads')
|
||||||
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.8.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true'])
|
spdlog = dependency('spdlog', version : ['>=1.8.5'], 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')
|
||||||
@ -94,7 +94,9 @@ 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'))
|
||||||
|
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
||||||
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
||||||
|
xkbregistry = dependency('xkbregistry')
|
||||||
|
|
||||||
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||||
if libsndio.found()
|
if libsndio.found()
|
||||||
@ -112,7 +114,11 @@ 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' ], modules : [ 'date::date', 'date::date-tz' ], 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')
|
sysconfdir = get_option('sysconfdir')
|
||||||
@ -136,7 +142,6 @@ src_files = files(
|
|||||||
'src/factory.cpp',
|
'src/factory.cpp',
|
||||||
'src/AModule.cpp',
|
'src/AModule.cpp',
|
||||||
'src/ALabel.cpp',
|
'src/ALabel.cpp',
|
||||||
'src/modules/clock.cpp',
|
|
||||||
'src/modules/custom.cpp',
|
'src/modules/custom.cpp',
|
||||||
'src/modules/disk.cpp',
|
'src/modules/disk.cpp',
|
||||||
'src/modules/idle_inhibitor.cpp',
|
'src/modules/idle_inhibitor.cpp',
|
||||||
@ -144,6 +149,7 @@ src_files = files(
|
|||||||
'src/main.cpp',
|
'src/main.cpp',
|
||||||
'src/bar.cpp',
|
'src/bar.cpp',
|
||||||
'src/client.cpp',
|
'src/client.cpp',
|
||||||
|
'src/util/ustring_clen.cpp'
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_linux
|
if is_linux
|
||||||
@ -211,6 +217,11 @@ if libudev.found() and (is_linux or libepoll.found())
|
|||||||
src_files += 'src/modules/backlight.cpp'
|
src_files += 'src/modules/backlight.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if libevdev.found() and (is_linux or libepoll.found())
|
||||||
|
add_project_arguments('-DHAVE_LIBEVDEV', language: 'cpp')
|
||||||
|
src_files += 'src/modules/keyboard_state.cpp'
|
||||||
|
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/mpd.cpp'
|
src_files += 'src/modules/mpd/mpd.cpp'
|
||||||
@ -236,6 +247,13 @@ if get_option('rfkill').enabled()
|
|||||||
endif
|
endif
|
||||||
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(
|
||||||
@ -259,9 +277,11 @@ executable(
|
|||||||
libudev,
|
libudev,
|
||||||
libepoll,
|
libepoll,
|
||||||
libmpdclient,
|
libmpdclient,
|
||||||
|
libevdev,
|
||||||
gtk_layer_shell,
|
gtk_layer_shell,
|
||||||
libsndio,
|
libsndio,
|
||||||
tz_dep
|
tz_dep,
|
||||||
|
xkbregistry
|
||||||
],
|
],
|
||||||
include_directories: [include_directories('include')],
|
include_directories: [include_directories('include')],
|
||||||
install: true,
|
install: true,
|
||||||
@ -299,11 +319,13 @@ if scdoc.found()
|
|||||||
'waybar-custom.5.scd',
|
'waybar-custom.5.scd',
|
||||||
'waybar-disk.5.scd',
|
'waybar-disk.5.scd',
|
||||||
'waybar-idle-inhibitor.5.scd',
|
'waybar-idle-inhibitor.5.scd',
|
||||||
|
'waybar-keyboard-state.5.scd',
|
||||||
'waybar-memory.5.scd',
|
'waybar-memory.5.scd',
|
||||||
'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-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',
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.')
|
option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.')
|
||||||
option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features')
|
option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features')
|
||||||
option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features')
|
option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features')
|
||||||
|
option('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features')
|
||||||
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
|
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
|
||||||
option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit')
|
option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit')
|
||||||
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
|
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
// 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", "sway/language", "battery", "battery#bat2", "clock", "tray"],
|
"modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "keyboard-state", "sway/language", "battery", "battery#bat2", "clock", "tray"],
|
||||||
// Modules configuration
|
// Modules configuration
|
||||||
// "sway/workspaces": {
|
// "sway/workspaces": {
|
||||||
// "disable-scroll": true,
|
// "disable-scroll": true,
|
||||||
@ -23,11 +23,20 @@
|
|||||||
// "default": ""
|
// "default": ""
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
|
"keyboard-state": {
|
||||||
|
"numlock": true,
|
||||||
|
"capslock": true,
|
||||||
|
"format": "{name} {icon}",
|
||||||
|
"format-icons": {
|
||||||
|
"locked": "",
|
||||||
|
"unlocked": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"sway/mode": {
|
"sway/mode": {
|
||||||
"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}) ⸨{songPosition}|{queueLength}⸩ ",
|
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ",
|
||||||
"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",
|
||||||
@ -145,3 +154,4 @@
|
|||||||
// "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name
|
// "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ window#waybar.chromium {
|
|||||||
#battery,
|
#battery,
|
||||||
#cpu,
|
#cpu,
|
||||||
#memory,
|
#memory,
|
||||||
|
#disk,
|
||||||
#temperature,
|
#temperature,
|
||||||
#backlight,
|
#backlight,
|
||||||
#network,
|
#network,
|
||||||
@ -107,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;
|
||||||
}
|
}
|
||||||
@ -142,6 +143,10 @@ label:focus {
|
|||||||
background-color: #9b59b6;
|
background-color: #9b59b6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#disk {
|
||||||
|
background-color: #964B00;
|
||||||
|
}
|
||||||
|
|
||||||
#backlight {
|
#backlight {
|
||||||
background-color: #90b1b1;
|
background-color: #90b1b1;
|
||||||
}
|
}
|
||||||
@ -190,6 +195,15 @@ label:focus {
|
|||||||
background-color: #2980b9;
|
background-color: #2980b9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tray > .passive {
|
||||||
|
-gtk-icon-effect: dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tray > .needs-attention {
|
||||||
|
-gtk-icon-effect: highlight;
|
||||||
|
background-color: #eb4d4b;
|
||||||
|
}
|
||||||
|
|
||||||
#idle_inhibitor {
|
#idle_inhibitor {
|
||||||
background-color: #2d3436;
|
background-color: #2d3436;
|
||||||
}
|
}
|
||||||
@ -223,3 +237,19 @@ label:focus {
|
|||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
min-width: 16px;
|
min-width: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#keyboard-state {
|
||||||
|
background: #97e1ad;
|
||||||
|
color: #000000;
|
||||||
|
padding: 0 0px;
|
||||||
|
margin: 0 5px;
|
||||||
|
min-width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#keyboard-state > label {
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#keyboard-state > label.locked {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
@ -3,9 +3,11 @@ Description=Highly customizable Wayland bar for Sway and Wlroots based composito
|
|||||||
Documentation=https://github.com/Alexays/Waybar/wiki/
|
Documentation=https://github.com/Alexays/Waybar/wiki/
|
||||||
PartOf=graphical-session.target
|
PartOf=graphical-session.target
|
||||||
After=graphical-session.target
|
After=graphical-session.target
|
||||||
|
Requisite=graphical-session.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=@prefix@/bin/waybar
|
ExecStart=@prefix@/bin/waybar
|
||||||
|
ExecReload=kill -SIGUSR2 $MAINPID
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
@ -20,7 +20,7 @@ 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);
|
label_.set_single_line_mode(true);
|
||||||
} else if (ellipsize && label_.get_max_width_chars() == -1) {
|
} else if (ellipsize && label_.get_max_width_chars() == -1) {
|
||||||
@ -28,9 +28,28 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
|||||||
label_.set_single_line_mode(true);
|
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 {
|
||||||
@ -57,17 +76,17 @@ 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) {
|
std::string ALabel::getIcon(uint16_t percentage, const std::vector<std::string>& alts, uint16_t max) {
|
||||||
auto format_icons = config_["format-icons"];
|
auto format_icons = config_["format-icons"];
|
||||||
if (format_icons.isObject()) {
|
if (format_icons.isObject()) {
|
||||||
|
std::string _alt = "default";
|
||||||
for (const auto& alt : alts) {
|
for (const auto& alt : alts) {
|
||||||
if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) {
|
if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) {
|
||||||
format_icons = format_icons[alt];
|
_alt = alt;
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
format_icons = format_icons["default"];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
format_icons = format_icons[_alt];
|
||||||
}
|
}
|
||||||
if (format_icons.isArray()) {
|
if (format_icons.isArray()) {
|
||||||
auto size = format_icons.size();
|
auto size = format_icons.size();
|
||||||
|
59
src/bar.cpp
59
src/bar.cpp
@ -33,6 +33,7 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable {
|
|||||||
gtk_layer_set_monitor(window_.gobj(), output.monitor->gobj());
|
gtk_layer_set_monitor(window_.gobj(), output.monitor->gobj());
|
||||||
gtk_layer_set_namespace(window_.gobj(), "waybar");
|
gtk_layer_set_namespace(window_.gobj(), "waybar");
|
||||||
|
|
||||||
|
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &GLSSurfaceImpl::onMap));
|
||||||
window.signal_configure_event().connect_notify(
|
window.signal_configure_event().connect_notify(
|
||||||
sigc::mem_fun(*this, &GLSSurfaceImpl::onConfigure));
|
sigc::mem_fun(*this, &GLSSurfaceImpl::onConfigure));
|
||||||
}
|
}
|
||||||
@ -62,6 +63,18 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable {
|
|||||||
gtk_layer_set_layer(window_.gobj(), layer);
|
gtk_layer_set_layer(window_.gobj(), layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setPassThrough(bool enable) override {
|
||||||
|
passthrough_ = enable;
|
||||||
|
auto gdk_window = window_.get_window();
|
||||||
|
if (gdk_window) {
|
||||||
|
Cairo::RefPtr<Cairo::Region> region;
|
||||||
|
if (enable) {
|
||||||
|
region = Cairo::Region::create();
|
||||||
|
}
|
||||||
|
gdk_window->input_shape_combine_region(region, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setPosition(const std::string_view& position) override {
|
void setPosition(const std::string_view& position) override {
|
||||||
auto unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM;
|
auto unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM;
|
||||||
vertical_ = false;
|
vertical_ = false;
|
||||||
@ -93,8 +106,11 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable {
|
|||||||
std::string output_name_;
|
std::string output_name_;
|
||||||
uint32_t width_;
|
uint32_t width_;
|
||||||
uint32_t height_;
|
uint32_t height_;
|
||||||
|
bool passthrough_ = false;
|
||||||
bool vertical_ = false;
|
bool vertical_ = false;
|
||||||
|
|
||||||
|
void onMap(GdkEventAny* ev) { setPassThrough(passthrough_); }
|
||||||
|
|
||||||
void onConfigure(GdkEventConfigure* ev) {
|
void onConfigure(GdkEventConfigure* ev) {
|
||||||
/*
|
/*
|
||||||
* GTK wants new size for the window.
|
* GTK wants new size for the window.
|
||||||
@ -182,6 +198,20 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setPassThrough(bool enable) override {
|
||||||
|
passthrough_ = enable;
|
||||||
|
/* GTK overwrites any region changes applied directly to the wl_surface,
|
||||||
|
* thus the same GTK region API as in the GLS impl has to be used. */
|
||||||
|
auto gdk_window = window_.get_window();
|
||||||
|
if (gdk_window) {
|
||||||
|
Cairo::RefPtr<Cairo::Region> region;
|
||||||
|
if (enable) {
|
||||||
|
region = Cairo::Region::create();
|
||||||
|
}
|
||||||
|
gdk_window->input_shape_combine_region(region, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setPosition(const std::string_view& position) override {
|
void setPosition(const std::string_view& position) override {
|
||||||
anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
||||||
if (position == "bottom") {
|
if (position == "bottom") {
|
||||||
@ -230,6 +260,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable {
|
|||||||
uint32_t height_ = 0;
|
uint32_t height_ = 0;
|
||||||
uint8_t anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
uint8_t anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
||||||
bool exclusive_zone_ = true;
|
bool exclusive_zone_ = true;
|
||||||
|
bool passthrough_ = false;
|
||||||
struct bar_margins margins_;
|
struct bar_margins margins_;
|
||||||
|
|
||||||
zwlr_layer_shell_v1_layer layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
|
zwlr_layer_shell_v1_layer layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
|
||||||
@ -262,6 +293,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable {
|
|||||||
|
|
||||||
setSurfaceSize(width_, height_);
|
setSurfaceSize(width_, height_);
|
||||||
setExclusiveZone(exclusive_zone_);
|
setExclusiveZone(exclusive_zone_);
|
||||||
|
setPassThrough(passthrough_);
|
||||||
|
|
||||||
commit();
|
commit();
|
||||||
wl_display_roundtrip(client->wl_display);
|
wl_display_roundtrip(client->wl_display);
|
||||||
@ -370,9 +402,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
window.get_style_context()->add_class(output->name);
|
window.get_style_context()->add_class(output->name);
|
||||||
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());
|
||||||
left_.get_style_context()->add_class("modules-left");
|
|
||||||
center_.get_style_context()->add_class("modules-center");
|
|
||||||
right_.get_style_context()->add_class("modules-right");
|
|
||||||
|
|
||||||
if (config["layer"] == "top") {
|
if (config["layer"] == "top") {
|
||||||
layer_ = bar_layer::TOP;
|
layer_ = bar_layer::TOP;
|
||||||
@ -380,6 +409,21 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
layer_ = bar_layer::OVERLAY;
|
layer_ = bar_layer::OVERLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config["exclusive"].isBool()) {
|
||||||
|
exclusive = config["exclusive"].asBool();
|
||||||
|
} else if (layer_ == bar_layer::OVERLAY) {
|
||||||
|
// swaybar defaults: overlay mode does not reserve an exclusive zone
|
||||||
|
exclusive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool passthrough = false;
|
||||||
|
if (config["passthrough"].isBool()) {
|
||||||
|
passthrough = config["passthrough"].asBool();
|
||||||
|
} else if (layer_ == bar_layer::OVERLAY) {
|
||||||
|
// swaybar defaults: overlay mode does not accept pointer events.
|
||||||
|
passthrough = true;
|
||||||
|
}
|
||||||
|
|
||||||
auto position = config["position"].asString();
|
auto position = config["position"].asString();
|
||||||
|
|
||||||
if (position == "right" || position == "left") {
|
if (position == "right" || position == "left") {
|
||||||
@ -390,6 +434,10 @@ 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 height = config["height"].isUInt() ? config["height"].asUInt() : 0;
|
||||||
uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0;
|
uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0;
|
||||||
|
|
||||||
@ -451,8 +499,9 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
surface_impl_->setLayer(layer_);
|
surface_impl_->setLayer(layer_);
|
||||||
surface_impl_->setExclusiveZone(true);
|
surface_impl_->setExclusiveZone(exclusive);
|
||||||
surface_impl_->setMargins(margins_);
|
surface_impl_->setMargins(margins_);
|
||||||
|
surface_impl_->setPassThrough(passthrough);
|
||||||
surface_impl_->setPosition(position);
|
surface_impl_->setPosition(position);
|
||||||
surface_impl_->setSize(width, height);
|
surface_impl_->setSize(width, height);
|
||||||
|
|
||||||
@ -491,7 +540,7 @@ void waybar::Bar::setVisible(bool value) {
|
|||||||
window.set_opacity(1);
|
window.set_opacity(1);
|
||||||
surface_impl_->setLayer(layer_);
|
surface_impl_->setLayer(layer_);
|
||||||
}
|
}
|
||||||
surface_impl_->setExclusiveZone(visible);
|
surface_impl_->setExclusiveZone(exclusive && visible);
|
||||||
surface_impl_->commit();
|
surface_impl_->commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
167
src/client.cpp
167
src/client.cpp
@ -1,12 +1,14 @@
|
|||||||
#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 "util/clara.hpp"
|
|
||||||
#include "util/json.hpp"
|
|
||||||
|
|
||||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
|
#include "util/clara.hpp"
|
||||||
|
#include "util/json.hpp"
|
||||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
waybar::Client *waybar::Client::inst() {
|
waybar::Client *waybar::Client::inst() {
|
||||||
@ -58,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());
|
||||||
@ -71,18 +73,20 @@ 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) {
|
||||||
if (config["output"].isArray()) {
|
if (config["output"].isArray()) {
|
||||||
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() &&
|
||||||
|
(output_conf.asString() == output.name || output_conf.asString() == output.identifier)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else if (config["output"].isString()) {
|
} else if (config["output"].isString()) {
|
||||||
auto config_output_name = config["output"].asString();
|
auto config_output = config["output"].asString();
|
||||||
if (!config_output_name.empty()) {
|
if (!config_output.empty()) {
|
||||||
if (config_output_name.substr(0, 1) == "!") {
|
if (config_output.substr(0, 1) == "!") {
|
||||||
return config_output_name.substr(1) != output.name;
|
return config_output.substr(1) != output.name &&
|
||||||
|
config_output.substr(1) != output.identifier;
|
||||||
}
|
}
|
||||||
return config_output_name == output.name;
|
return config_output == output.name || config_output == output.identifier;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,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()) {
|
void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * /*xdg_output*/,
|
||||||
output.xdg_output.reset();
|
const char *description) {
|
||||||
} else {
|
auto client = waybar::Client::inst();
|
||||||
wl_display_roundtrip(client->wl_display);
|
try {
|
||||||
for (const auto &config : configs) {
|
auto & output = client->getOutput(data);
|
||||||
client->bars.emplace_back(std::make_unique<Bar>(&output, config));
|
const char *open_paren = strrchr(description, '(');
|
||||||
Glib::RefPtr<Gdk::Screen> screen = client->bars.back()->window.get_screen();
|
|
||||||
client->style_context_->add_provider_for_screen(
|
// Description format: "identifier (name)"
|
||||||
screen, client->css_provider_, GTK_STYLE_PROVIDER_PRIORITY_USER);
|
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;
|
||||||
}
|
}
|
||||||
@ -147,6 +179,16 @@ 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;
|
||||||
@ -165,9 +207,13 @@ std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
|
|||||||
const std::string &config, const std::string &style) const {
|
const std::string &config, const std::string &style) const {
|
||||||
auto config_file = config.empty() ? getValidPath({
|
auto config_file = config.empty() ? getValidPath({
|
||||||
"$XDG_CONFIG_HOME/waybar/config",
|
"$XDG_CONFIG_HOME/waybar/config",
|
||||||
|
"$XDG_CONFIG_HOME/waybar/config.jsonc",
|
||||||
"$HOME/.config/waybar/config",
|
"$HOME/.config/waybar/config",
|
||||||
|
"$HOME/.config/waybar/config.jsonc",
|
||||||
"$HOME/waybar/config",
|
"$HOME/waybar/config",
|
||||||
|
"$HOME/waybar/config.jsonc",
|
||||||
"/etc/xdg/waybar/config",
|
"/etc/xdg/waybar/config",
|
||||||
|
"/etc/xdg/waybar/config.jsonc",
|
||||||
SYSCONFDIR "/xdg/waybar/config",
|
SYSCONFDIR "/xdg/waybar/config",
|
||||||
"./resources/config",
|
"./resources/config",
|
||||||
})
|
})
|
||||||
@ -188,14 +234,62 @@ std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
|
|||||||
return {config_file, css_file};
|
return {config_file, css_file};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::Client::setupConfig(const std::string &config_file) -> void {
|
auto waybar::Client::setupConfig(const std::string &config_file, int depth) -> void {
|
||||||
|
if (depth > 100) {
|
||||||
|
throw std::runtime_error("Aborting due to likely recursive include in config files");
|
||||||
|
}
|
||||||
std::ifstream file(config_file);
|
std::ifstream file(config_file);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
throw std::runtime_error("Can't open config file");
|
throw std::runtime_error("Can't open config file");
|
||||||
}
|
}
|
||||||
std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
util::JsonParser parser;
|
util::JsonParser parser;
|
||||||
config_ = parser.parse(str);
|
Json::Value tmp_config_ = parser.parse(str);
|
||||||
|
if (tmp_config_.isArray()) {
|
||||||
|
for (auto &config_part : tmp_config_) {
|
||||||
|
resolveConfigIncludes(config_part, depth);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolveConfigIncludes(tmp_config_, depth);
|
||||||
|
}
|
||||||
|
mergeConfig(config_, tmp_config_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::Client::resolveConfigIncludes(Json::Value &config, int depth) -> void {
|
||||||
|
Json::Value includes = config["include"];
|
||||||
|
if (includes.isArray()) {
|
||||||
|
for (const auto &include : includes) {
|
||||||
|
spdlog::info("Including resource file: {}", include.asString());
|
||||||
|
setupConfig(getValidPath({include.asString()}), ++depth);
|
||||||
|
}
|
||||||
|
} else if (includes.isString()) {
|
||||||
|
spdlog::info("Including resource file: {}", includes.asString());
|
||||||
|
setupConfig(getValidPath({includes.asString()}), ++depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void {
|
||||||
|
if (!a_config_) {
|
||||||
|
// For the first config
|
||||||
|
a_config_ = b_config_;
|
||||||
|
} else if (a_config_.isObject() && b_config_.isObject()) {
|
||||||
|
for (const auto &key : b_config_.getMemberNames()) {
|
||||||
|
if (a_config_[key].isObject() && b_config_[key].isObject()) {
|
||||||
|
mergeConfig(a_config_[key], b_config_[key]);
|
||||||
|
} else {
|
||||||
|
a_config_[key] = b_config_[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (a_config_.isArray() && b_config_.isArray()) {
|
||||||
|
// This can happen only on the top-level array of a multi-bar config
|
||||||
|
for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) {
|
||||||
|
if (a_config_[i].isObject() && b_config_[i].isObject()) {
|
||||||
|
mergeConfig(a_config_[i], b_config_[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spdlog::error("Cannot merge config, conflicting or invalid JSON types");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::Client::setupCss(const std::string &css_file) -> void {
|
auto waybar::Client::setupCss(const std::string &css_file) -> void {
|
||||||
@ -206,6 +300,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() {
|
||||||
@ -260,7 +357,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", Gio::APPLICATION_HANDLES_COMMAND_LINE);
|
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");
|
||||||
@ -270,16 +368,15 @@ int waybar::Client::main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj());
|
wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj());
|
||||||
auto [config_file, css_file] = getConfigs(config, style);
|
auto [config_file, css_file] = getConfigs(config, style);
|
||||||
setupConfig(config_file);
|
setupConfig(config_file, 0);
|
||||||
setupCss(css_file);
|
setupCss(css_file);
|
||||||
bindInterfaces();
|
bindInterfaces();
|
||||||
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();
|
||||||
|
}
|
||||||
|
@ -70,6 +70,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
return new waybar::modules::Backlight(id, config_[name]);
|
return new waybar::modules::Backlight(id, config_[name]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_LIBEVDEV
|
||||||
|
if (ref == "keyboard-state") {
|
||||||
|
return new waybar::modules::KeyboardState(id, bar_, config_[name]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef HAVE_LIBPULSE
|
#ifdef HAVE_LIBPULSE
|
||||||
if (ref == "pulseaudio") {
|
if (ref == "pulseaudio") {
|
||||||
return new waybar::modules::Pulseaudio(id, config_[name]);
|
return new waybar::modules::Pulseaudio(id, config_[name]);
|
||||||
|
15
src/main.cpp
15
src/main.cpp
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
std::mutex reap_mtx;
|
std::mutex reap_mtx;
|
||||||
std::list<pid_t> reap;
|
std::list<pid_t> reap;
|
||||||
|
volatile bool reload;
|
||||||
|
|
||||||
void* signalThread(void* args) {
|
void* signalThread(void* args) {
|
||||||
int err, signum;
|
int err, signum;
|
||||||
@ -70,12 +71,19 @@ void startSignalThread(void) {
|
|||||||
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) {
|
||||||
@ -85,7 +93,12 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
startSignalThread();
|
startSignalThread();
|
||||||
|
|
||||||
auto ret = client->main(argc, argv);
|
auto ret = 0;
|
||||||
|
do {
|
||||||
|
reload = false;
|
||||||
|
ret = client->main(argc, argv);
|
||||||
|
} while (reload);
|
||||||
|
|
||||||
delete client;
|
delete client;
|
||||||
return ret;
|
return ret;
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -135,33 +135,73 @@ void waybar::modules::Battery::refreshBatteries() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::tuple<uint8_t, float, std::string> waybar::modules::Battery::getInfos() {
|
// 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_);
|
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
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& item : batteries_) {
|
for (auto const& item : batteries_) {
|
||||||
auto bat = item.first;
|
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 / "status") >> _status;
|
std::ifstream(bat / "status") >> _status;
|
||||||
auto rate_path = fs::exists(bat / "current_now") ? "current_now" : "power_now";
|
|
||||||
std::ifstream(bat / rate_path) >> power_now;
|
// Some battery will report current and charge in μA/μAh.
|
||||||
auto now_path = fs::exists(bat / "charge_now") ? "charge_now" : "energy_now";
|
// Scale these by the voltage to get μW/μWh.
|
||||||
std::ifstream(bat / now_path) >> energy_now;
|
if (fs::exists(bat / "current_now")) {
|
||||||
auto full_path = fs::exists(bat / "charge_full") ? "charge_full" : "energy_full";
|
uint32_t voltage_now;
|
||||||
std::ifstream(bat / full_path) >> energy_full;
|
uint32_t current_now;
|
||||||
if (_status != "Unknown") {
|
uint32_t charge_now;
|
||||||
|
uint32_t charge_full;
|
||||||
|
uint32_t charge_full_design;
|
||||||
|
std::ifstream(bat / "voltage_now") >> voltage_now;
|
||||||
|
std::ifstream(bat / "current_now") >> current_now;
|
||||||
|
std::ifstream(bat / "charge_full") >> charge_full;
|
||||||
|
std::ifstream(bat / "charge_full_design") >> charge_full_design;
|
||||||
|
if (fs::exists(bat / "charge_now"))
|
||||||
|
std::ifstream(bat / "charge_now") >> charge_now;
|
||||||
|
else {
|
||||||
|
// charge_now is missing on some systems, estimate using capacity.
|
||||||
|
uint32_t capacity;
|
||||||
|
std::ifstream(bat / "capacity") >> capacity;
|
||||||
|
charge_now = (capacity * charge_full) / 100;
|
||||||
|
}
|
||||||
|
power_now = ((uint64_t)current_now * (uint64_t)voltage_now) / 1000000;
|
||||||
|
energy_now = ((uint64_t)charge_now * (uint64_t)voltage_now) / 1000000;
|
||||||
|
energy_full = ((uint64_t)charge_full * (uint64_t)voltage_now) / 1000000;
|
||||||
|
energy_full_design = ((uint64_t)charge_full_design * (uint64_t)voltage_now) / 1000000;
|
||||||
|
} else {
|
||||||
|
std::ifstream(bat / "power_now") >> power_now;
|
||||||
|
std::ifstream(bat / "energy_now") >> energy_now;
|
||||||
|
std::ifstream(bat / "energy_full") >> energy_full;
|
||||||
|
std::ifstream(bat / "energy_full_design") >> energy_full_design;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the "smallest" status among all batteries
|
||||||
|
if (status_gt(status, _status)) {
|
||||||
status = _status;
|
status = _status;
|
||||||
}
|
}
|
||||||
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;
|
||||||
@ -182,6 +222,10 @@ const std::tuple<uint8_t, float, std::string> waybar::modules::Battery::getInfos
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
float capacity = ((float)total_energy * 100.0f / (float) total_energy_full);
|
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
|
// Handle full-at
|
||||||
if (config_["full-at"].isUInt()) {
|
if (config_["full-at"].isUInt()) {
|
||||||
auto full_at = config_["full-at"].asUInt();
|
auto full_at = config_["full-at"].asUInt();
|
||||||
@ -195,16 +239,16 @@ const std::tuple<uint8_t, float, std::string> waybar::modules::Battery::getInfos
|
|||||||
capacity = 100.f;
|
capacity = 100.f;
|
||||||
}
|
}
|
||||||
uint8_t cap = round(capacity);
|
uint8_t cap = round(capacity);
|
||||||
if (cap == 100) {
|
if (cap == 100 && status == "Charging") {
|
||||||
// If we've reached 100% just mark as full as some batteries can stay
|
// If we've reached 100% just mark as full as some batteries can stay
|
||||||
// stuck reporting they're still charging but not yet done
|
// stuck reporting they're still charging but not yet done
|
||||||
status = "Full";
|
status = "Full";
|
||||||
}
|
}
|
||||||
|
|
||||||
return {cap, time_remaining, status};
|
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};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +268,7 @@ const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) c
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemaining) {
|
const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemaining) {
|
||||||
hoursRemaining = std::fabs(hoursRemaining);
|
hoursRemaining = std::fabs(hoursRemaining);
|
||||||
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");
|
||||||
@ -239,26 +283,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;
|
|
||||||
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 and replace space with dash
|
// Transform to lowercase and 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 == ' ' ? '-' : std::tolower(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_);
|
||||||
}
|
}
|
||||||
@ -278,8 +337,9 @@ auto waybar::modules::Battery::update() -> void {
|
|||||||
auto icons = std::vector<std::string>{status + "-" + state, status, state};
|
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("power", power),
|
||||||
fmt::arg("icon", getIcon(capacity, icons)),
|
fmt::arg("icon", getIcon(capacity, icons)),
|
||||||
fmt::arg("time", formatTimeRemaining(time_remaining))));
|
fmt::arg("time", time_remaining_formatted)));
|
||||||
}
|
}
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
|
@ -1,45 +1,25 @@
|
|||||||
#include "modules/bluetooth.hpp"
|
#include "modules/bluetooth.hpp"
|
||||||
#include "util/rfkill.hpp"
|
|
||||||
#include <linux/rfkill.h>
|
#include <fmt/format.h>
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config)
|
waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config)
|
||||||
: ALabel(config, "bluetooth", id, "{icon}", 10),
|
: ALabel(config, "bluetooth", id, "{icon}", 10), rfkill_{RFKILL_TYPE_BLUETOOTH} {
|
||||||
status_("disabled"),
|
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
|
||||||
rfkill_{RFKILL_TYPE_BLUETOOTH} {
|
|
||||||
thread_ = [this] {
|
|
||||||
dp.emit();
|
|
||||||
rfkill_.waitForEvent();
|
|
||||||
};
|
|
||||||
intervall_thread_ = [this] {
|
|
||||||
auto now = std::chrono::system_clock::now();
|
|
||||||
auto timeout = std::chrono::floor<std::chrono::seconds>(now + interval_);
|
|
||||||
auto diff = std::chrono::seconds(timeout.time_since_epoch().count() % interval_.count());
|
|
||||||
thread_.sleep_until(timeout - diff);
|
|
||||||
dp.emit();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Bluetooth::update() -> void {
|
auto waybar::modules::Bluetooth::update() -> void {
|
||||||
if (rfkill_.getState()) {
|
std::string status = rfkill_.getState() ? "disabled" : "enabled";
|
||||||
status_ = "disabled";
|
|
||||||
} else {
|
|
||||||
status_ = "enabled";
|
|
||||||
}
|
|
||||||
|
|
||||||
label_.set_markup(
|
label_.set_markup(
|
||||||
fmt::format(
|
fmt::format(format_, fmt::arg("status", status), fmt::arg("icon", getIcon(0, status))));
|
||||||
format_,
|
|
||||||
fmt::arg("status", status_),
|
|
||||||
fmt::arg("icon", getIcon(0, status_))));
|
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
if (config_["tooltip-format"].isString()) {
|
if (config_["tooltip-format"].isString()) {
|
||||||
auto tooltip_format = config_["tooltip-format"].asString();
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
auto tooltip_text = fmt::format(tooltip_format, status_);
|
auto tooltip_text = fmt::format(tooltip_format, status, fmt::arg("status", status));
|
||||||
label_.set_tooltip_text(tooltip_text);
|
label_.set_tooltip_text(tooltip_text);
|
||||||
} else {
|
} else {
|
||||||
label_.set_tooltip_text(status_);
|
label_.set_tooltip_text(status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#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>
|
||||||
@ -154,12 +155,14 @@ auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std
|
|||||||
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 wd_len = wd_ustring.length();
|
auto clen = ustring_clen(wd_ustring);
|
||||||
if (wd_len > 2) {
|
auto wd_len = wd_ustring.length();
|
||||||
wd_ustring = wd_ustring.substr(0, 2);
|
while (clen > 2) {
|
||||||
wd_len = 2;
|
wd_ustring = wd_ustring.substr(0, wd_len-1);
|
||||||
|
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";
|
||||||
@ -193,6 +196,9 @@ template <>
|
|||||||
struct fmt::formatter<waybar_time> : fmt::formatter<std::tm> {
|
struct fmt::formatter<waybar_time> : fmt::formatter<std::tm> {
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const waybar_time& t, FormatContext& ctx) {
|
auto format(const waybar_time& t, FormatContext& ctx) {
|
||||||
|
#if FMT_VERSION >= 80000
|
||||||
|
auto& tm_format = specs;
|
||||||
|
#endif
|
||||||
return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime));
|
return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
#include <cstdlib> // malloc
|
#include <cstdlib> // malloc
|
||||||
#include <unistd.h> // sysconf
|
#include <unistd.h> // sysconf
|
||||||
|
#include <cmath> // NAN
|
||||||
|
|
||||||
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
||||||
# include <sys/sched.h>
|
# include <sys/sched.h>
|
||||||
@ -95,3 +97,12 @@ std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo() {
|
|||||||
free(cp_time);
|
free(cp_time);
|
||||||
return cpuinfo;
|
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;
|
||||||
|
}
|
||||||
|
@ -12,19 +12,36 @@ auto waybar::modules::Cpu::update() -> void {
|
|||||||
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
|
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
|
||||||
auto cpu_load = getCpuLoad();
|
auto cpu_load = getCpuLoad();
|
||||||
auto [cpu_usage, tooltip] = getCpuUsage();
|
auto [cpu_usage, tooltip] = getCpuUsage();
|
||||||
|
auto [max_frequency, min_frequency, avg_frequency] = getCpuFrequency();
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
label_.set_tooltip_text(tooltip);
|
label_.set_tooltip_text(tooltip);
|
||||||
}
|
}
|
||||||
label_.set_markup(fmt::format(format_, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage)));
|
auto format = format_;
|
||||||
getState(cpu_usage);
|
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
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t waybar::modules::Cpu::getCpuLoad() {
|
double waybar::modules::Cpu::getCpuLoad() {
|
||||||
double load[1];
|
double load[1];
|
||||||
if (getloadavg(load, 1) != -1) {
|
if (getloadavg(load, 1) != -1) {
|
||||||
return load[0] * 100 / sysconf(_SC_NPROCESSORS_ONLN);
|
return load[0];
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Can't get Cpu load");
|
throw std::runtime_error("Can't get Cpu load");
|
||||||
}
|
}
|
||||||
@ -53,3 +70,16 @@ std::tuple<uint16_t, std::string> waybar::modules::Cpu::getCpuUsage() {
|
|||||||
prev_times_ = curr_times;
|
prev_times_ = curr_times;
|
||||||
return {usage, tooltip};
|
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 };
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <filesystem>
|
||||||
#include "modules/cpu.hpp"
|
#include "modules/cpu.hpp"
|
||||||
|
|
||||||
std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo() {
|
std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo() {
|
||||||
@ -27,3 +28,50 @@ std::vector<std::tuple<size_t, size_t>> waybar::modules::Cpu::parseCpuinfo() {
|
|||||||
}
|
}
|
||||||
return cpuinfo;
|
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();
|
||||||
|
|
||||||
|
if (frequencies.size() <= 0) {
|
||||||
|
std::string cpufreq_dir = "/sys/devices/system/cpu/cpufreq";
|
||||||
|
if (std::filesystem::exists(cpufreq_dir)) {
|
||||||
|
std::vector<std::string> frequency_files = {
|
||||||
|
"/cpuinfo_min_freq",
|
||||||
|
"/cpuinfo_max_freq"
|
||||||
|
};
|
||||||
|
for (auto& p: std::filesystem::directory_iterator(cpufreq_dir)) {
|
||||||
|
for (auto freq_file: frequency_files) {
|
||||||
|
std::string freq_file_path = p.path().string() + freq_file;
|
||||||
|
if (std::filesystem::exists(freq_file_path)) {
|
||||||
|
std::string freq_value;
|
||||||
|
std::ifstream freq(freq_file_path);
|
||||||
|
if (freq.is_open()) {
|
||||||
|
getline(freq, freq_value);
|
||||||
|
float frequency = std::strtol(freq_value.c_str(), nullptr, 10);
|
||||||
|
frequencies.push_back(frequency / 1000);
|
||||||
|
freq.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return frequencies;
|
||||||
|
}
|
||||||
|
@ -49,15 +49,27 @@ auto waybar::modules::Disk::update() -> void {
|
|||||||
auto total = pow_format(stats.f_blocks * stats.f_frsize, "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;
|
auto percentage_used = (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks;
|
||||||
|
|
||||||
label_.set_markup(fmt::format(format_
|
auto format = format_;
|
||||||
, stats.f_bavail * 100 / stats.f_blocks
|
auto state = getState(percentage_used);
|
||||||
, fmt::arg("free", free)
|
if (!state.empty() && config_["format-" + state].isString()) {
|
||||||
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
|
format = config_["format-" + state].asString();
|
||||||
, fmt::arg("used", used)
|
}
|
||||||
, fmt::arg("percentage_used", percentage_used)
|
|
||||||
, fmt::arg("total", total)
|
if (format.empty()) {
|
||||||
, fmt::arg("path", path_)
|
event_box_.hide();
|
||||||
));
|
} else {
|
||||||
|
event_box_.show();
|
||||||
|
label_.set_markup(fmt::format(format
|
||||||
|
, stats.f_bavail * 100 / stats.f_blocks
|
||||||
|
, fmt::arg("free", free)
|
||||||
|
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
|
||||||
|
, fmt::arg("used", used)
|
||||||
|
, fmt::arg("percentage_used", percentage_used)
|
||||||
|
, fmt::arg("total", total)
|
||||||
|
, fmt::arg("path", path_)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
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()) {
|
||||||
@ -73,8 +85,6 @@ auto waybar::modules::Disk::update() -> void {
|
|||||||
, fmt::arg("path", path_)
|
, fmt::arg("path", path_)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
event_box_.show();
|
|
||||||
getState(percentage_used);
|
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar&
|
|||||||
bar_(bar),
|
bar_(bar),
|
||||||
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));
|
||||||
|
152
src/modules/keyboard_state.cpp
Normal file
152
src/modules/keyboard_state.cpp
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
#include "modules/keyboard_state.hpp"
|
||||||
|
#include <filesystem>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||||
|
: AModule(config, "keyboard-state", id, false, !config["disable-scroll"].asBool()),
|
||||||
|
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||||
|
numlock_label_(""),
|
||||||
|
capslock_label_(""),
|
||||||
|
numlock_format_(config_["format"].isString() ? config_["format"].asString()
|
||||||
|
: config_["format"]["numlock"].isString() ? config_["format"]["numlock"].asString()
|
||||||
|
: "{name} {icon}"),
|
||||||
|
capslock_format_(config_["format"].isString() ? config_["format"].asString()
|
||||||
|
: config_["format"]["capslock"].isString() ? config_["format"]["capslock"].asString()
|
||||||
|
: "{name} {icon}"),
|
||||||
|
scrolllock_format_(config_["format"].isString() ? config_["format"].asString()
|
||||||
|
: config_["format"]["scrolllock"].isString() ? config_["format"]["scrolllock"].asString()
|
||||||
|
: "{name} {icon}"),
|
||||||
|
interval_(std::chrono::seconds(config_["interval"].isUInt() ? config_["interval"].asUInt() : 1)),
|
||||||
|
icon_locked_(config_["format-icons"]["locked"].isString()
|
||||||
|
? config_["format-icons"]["locked"].asString()
|
||||||
|
: "locked"),
|
||||||
|
icon_unlocked_(config_["format-icons"]["unlocked"].isString()
|
||||||
|
? config_["format-icons"]["unlocked"].asString()
|
||||||
|
: "unlocked"),
|
||||||
|
fd_(0),
|
||||||
|
dev_(nullptr) {
|
||||||
|
box_.set_name("keyboard-state");
|
||||||
|
if (config_["numlock"].asBool()) {
|
||||||
|
box_.pack_end(numlock_label_, false, false, 0);
|
||||||
|
}
|
||||||
|
if (config_["capslock"].asBool()) {
|
||||||
|
box_.pack_end(capslock_label_, false, false, 0);
|
||||||
|
}
|
||||||
|
if (config_["scrolllock"].asBool()) {
|
||||||
|
box_.pack_end(scrolllock_label_, false, false, 0);
|
||||||
|
}
|
||||||
|
if (!id.empty()) {
|
||||||
|
box_.get_style_context()->add_class(id);
|
||||||
|
}
|
||||||
|
event_box_.add(box_);
|
||||||
|
|
||||||
|
if (config_["device-path"].isString()) {
|
||||||
|
std::string dev_path = config_["device-path"].asString();
|
||||||
|
std::tie(fd_, dev_) = openDevice(dev_path);
|
||||||
|
} else {
|
||||||
|
DIR* dev_dir = opendir("/dev/input");
|
||||||
|
if (dev_dir == nullptr) {
|
||||||
|
throw std::runtime_error("Failed to open /dev/input");
|
||||||
|
}
|
||||||
|
dirent *ep;
|
||||||
|
while ((ep = readdir(dev_dir))) {
|
||||||
|
if (ep->d_type != DT_CHR) continue;
|
||||||
|
std::string dev_path = std::string("/dev/input/") + ep->d_name;
|
||||||
|
try {
|
||||||
|
std::tie(fd_, dev_) = openDevice(dev_path);
|
||||||
|
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev_), dev_path);
|
||||||
|
break;
|
||||||
|
} catch (const std::runtime_error& e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dev_ == nullptr) {
|
||||||
|
throw std::runtime_error("Failed to find keyboard device");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
thread_.sleep_for(interval_);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
waybar::modules::KeyboardState::~KeyboardState() {
|
||||||
|
libevdev_free(dev_);
|
||||||
|
int err = close(fd_);
|
||||||
|
if (err < 0) {
|
||||||
|
// Not much we can do, so ignore it.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::KeyboardState::openDevice(const std::string& path) -> std::pair<int, libevdev*> {
|
||||||
|
int fd = open(path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
throw std::runtime_error("Can't open " + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
libevdev* dev;
|
||||||
|
int err = libevdev_new_from_fd(fd, &dev);
|
||||||
|
if (err < 0) {
|
||||||
|
throw std::runtime_error("Can't create libevdev device");
|
||||||
|
}
|
||||||
|
if (!libevdev_has_event_type(dev, EV_LED)) {
|
||||||
|
throw std::runtime_error("Device doesn't support LED events");
|
||||||
|
}
|
||||||
|
if (!libevdev_has_event_code(dev, EV_LED, LED_NUML)
|
||||||
|
|| !libevdev_has_event_code(dev, EV_LED, LED_CAPSL)
|
||||||
|
|| !libevdev_has_event_code(dev, EV_LED, LED_SCROLLL)) {
|
||||||
|
throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(fd, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::KeyboardState::update() -> void {
|
||||||
|
int err = LIBEVDEV_READ_STATUS_SUCCESS;
|
||||||
|
while (err == LIBEVDEV_READ_STATUS_SUCCESS) {
|
||||||
|
input_event ev;
|
||||||
|
err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_NORMAL, &ev);
|
||||||
|
while (err == LIBEVDEV_READ_STATUS_SYNC) {
|
||||||
|
err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (err != -EAGAIN) {
|
||||||
|
throw std::runtime_error("Failed to sync evdev device");
|
||||||
|
}
|
||||||
|
|
||||||
|
int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML);
|
||||||
|
int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL);
|
||||||
|
int scrolll = libevdev_get_event_value(dev_, EV_LED, LED_SCROLLL);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool state;
|
||||||
|
Gtk::Label& label;
|
||||||
|
const std::string& format;
|
||||||
|
const char* name;
|
||||||
|
} label_states[] = {
|
||||||
|
{(bool) numl, numlock_label_, numlock_format_, "Num"},
|
||||||
|
{(bool) capsl, capslock_label_, capslock_format_, "Caps"},
|
||||||
|
{(bool) scrolll, scrolllock_label_, scrolllock_format_, "Scroll"},
|
||||||
|
};
|
||||||
|
for (auto& label_state : label_states) {
|
||||||
|
std::string text;
|
||||||
|
text = fmt::format(label_state.format,
|
||||||
|
fmt::arg("icon", label_state.state ? icon_locked_ : icon_unlocked_),
|
||||||
|
fmt::arg("name", label_state.name));
|
||||||
|
label_state.label.set_markup(text);
|
||||||
|
if (label_state.state) {
|
||||||
|
label_state.label.get_style_context()->add_class("locked");
|
||||||
|
} else {
|
||||||
|
label_state.label.get_style_context()->remove_class("locked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AModule::update();
|
||||||
|
}
|
@ -28,13 +28,24 @@ auto waybar::modules::Memory::update() -> void {
|
|||||||
auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2);
|
auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2);
|
||||||
auto available_ram_gigabytes = memfree / std::pow(1024, 2);
|
auto available_ram_gigabytes = memfree / std::pow(1024, 2);
|
||||||
|
|
||||||
getState(used_ram_percentage);
|
auto format = format_;
|
||||||
label_.set_markup(fmt::format(format_,
|
auto state = getState(used_ram_percentage);
|
||||||
used_ram_percentage,
|
if (!state.empty() && config_["format-" + state].isString()) {
|
||||||
fmt::arg("total", total_ram_gigabytes),
|
format = config_["format-" + state].asString();
|
||||||
fmt::arg("percentage", used_ram_percentage),
|
}
|
||||||
fmt::arg("used", used_ram_gigabytes),
|
|
||||||
fmt::arg("avail", available_ram_gigabytes)));
|
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 (tooltipEnabled()) {
|
||||||
if (config_["tooltip-format"].isString()) {
|
if (config_["tooltip-format"].isString()) {
|
||||||
auto tooltip_format = config_["tooltip-format"].asString();
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
@ -48,7 +59,6 @@ auto waybar::modules::Memory::update() -> void {
|
|||||||
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event_box_.show();
|
|
||||||
} else {
|
} else {
|
||||||
event_box_.hide();
|
event_box_.hide();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#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"
|
#include "modules/mpd/state.hpp"
|
||||||
#if defined(MPD_NOINLINE)
|
#if defined(MPD_NOINLINE)
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
@ -98,9 +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;
|
int song_pos = 0, queue_length = 0, volume = 0;
|
||||||
std::chrono::seconds elapsedTime, totalTime;
|
std::chrono::seconds elapsedTime, totalTime;
|
||||||
|
|
||||||
std::string stateIcon = "";
|
std::string stateIcon = "";
|
||||||
@ -130,6 +130,7 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
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());
|
song_pos = mpd_status_get_song_pos(status_.get());
|
||||||
|
volume = mpd_status_get_volume(status_.get());
|
||||||
queue_length = mpd_status_get_queue_length(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()));
|
||||||
@ -143,6 +144,10 @@ 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());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
label_.set_markup(
|
label_.set_markup(
|
||||||
@ -152,6 +157,7 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
fmt::arg("album", Glib::Markup::escape_text(album).raw()),
|
fmt::arg("album", Glib::Markup::escape_text(album).raw()),
|
||||||
fmt::arg("title", Glib::Markup::escape_text(title).raw()),
|
fmt::arg("title", Glib::Markup::escape_text(title).raw()),
|
||||||
fmt::arg("date", Glib::Markup::escape_text(date).raw()),
|
fmt::arg("date", Glib::Markup::escape_text(date).raw()),
|
||||||
|
fmt::arg("volume", volume),
|
||||||
fmt::arg("elapsedTime", elapsedTime),
|
fmt::arg("elapsedTime", elapsedTime),
|
||||||
fmt::arg("totalTime", totalTime),
|
fmt::arg("totalTime", totalTime),
|
||||||
fmt::arg("songPosition", song_pos),
|
fmt::arg("songPosition", song_pos),
|
||||||
@ -171,11 +177,12 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
: "MPD (connected)";
|
: "MPD (connected)";
|
||||||
try {
|
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("volume", volume),
|
||||||
fmt::arg("elapsedTime", elapsedTime),
|
fmt::arg("elapsedTime", elapsedTime),
|
||||||
fmt::arg("totalTime", totalTime),
|
fmt::arg("totalTime", totalTime),
|
||||||
fmt::arg("songPosition", song_pos),
|
fmt::arg("songPosition", song_pos),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "modules/network.hpp"
|
#include "modules/network.hpp"
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <sys/eventfd.h>
|
#include <sys/eventfd.h>
|
||||||
|
#include <linux/if.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@ -18,6 +19,7 @@ constexpr const char *NETSTAT_FILE =
|
|||||||
constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt";
|
constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt";
|
||||||
constexpr std::string_view BANDWIDTH_DOWN_TOTAL_KEY = "InOctets";
|
constexpr std::string_view BANDWIDTH_DOWN_TOTAL_KEY = "InOctets";
|
||||||
constexpr std::string_view BANDWIDTH_UP_TOTAL_KEY = "OutOctets";
|
constexpr std::string_view BANDWIDTH_UP_TOTAL_KEY = "OutOctets";
|
||||||
|
constexpr const char *DEFAULT_FORMAT = "{ifname}";
|
||||||
|
|
||||||
std::ifstream netstat(NETSTAT_FILE);
|
std::ifstream netstat(NETSTAT_FILE);
|
||||||
std::optional<unsigned long long> read_netstat(std::string_view category, std::string_view key) {
|
std::optional<unsigned long long> read_netstat(std::string_view category, std::string_view key) {
|
||||||
@ -81,18 +83,29 @@ std::optional<unsigned long long> read_netstat(std::string_view category, std::s
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
|
waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
|
||||||
: ALabel(config, "network", id, "{ifname}", 60),
|
: ALabel(config, "network", id, DEFAULT_FORMAT, 60),
|
||||||
ifid_(-1),
|
ifid_(-1),
|
||||||
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
|
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
|
||||||
efd_(-1),
|
efd_(-1),
|
||||||
ev_fd_(-1),
|
ev_fd_(-1),
|
||||||
cidr_(-1),
|
want_route_dump_(false),
|
||||||
|
want_link_dump_(false),
|
||||||
|
want_addr_dump_(false),
|
||||||
|
dump_in_progress_(false),
|
||||||
|
cidr_(0),
|
||||||
signal_strength_dbm_(0),
|
signal_strength_dbm_(0),
|
||||||
signal_strength_(0),
|
signal_strength_(0),
|
||||||
#ifdef WANT_RFKILL
|
#ifdef WANT_RFKILL
|
||||||
rfkill_{RFKILL_TYPE_WLAN},
|
rfkill_{RFKILL_TYPE_WLAN},
|
||||||
#endif
|
#endif
|
||||||
frequency_(0) {
|
frequency_(0) {
|
||||||
|
|
||||||
|
// Start with some "text" in the module's label_, update() will then
|
||||||
|
// update it. Since the text should be different, update() will be able
|
||||||
|
// to show or hide the event_box_. This is to work around the case where
|
||||||
|
// the module start with no text, but the the event_box_ is shown.
|
||||||
|
label_.set_markup("<s></s>");
|
||||||
|
|
||||||
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);
|
||||||
if (down_octets) {
|
if (down_octets) {
|
||||||
@ -107,17 +120,25 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
|||||||
bandwidth_up_total_ = 0;
|
bandwidth_up_total_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!config_["interface"].isString()) {
|
||||||
|
// "interface" isn't configure, then try to guess the external
|
||||||
|
// interface currently used for internet.
|
||||||
|
want_route_dump_ = true;
|
||||||
|
} else {
|
||||||
|
// Look for an interface that match "interface"
|
||||||
|
// and then find the address associated with it.
|
||||||
|
want_link_dump_ = true;
|
||||||
|
want_addr_dump_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
createEventSocket();
|
createEventSocket();
|
||||||
createInfoSocket();
|
createInfoSocket();
|
||||||
auto default_iface = getPreferredIface(-1, false);
|
|
||||||
if (default_iface != -1) {
|
|
||||||
ifid_ = default_iface;
|
|
||||||
char ifname[IF_NAMESIZE];
|
|
||||||
if_indextoname(default_iface, ifname);
|
|
||||||
ifname_ = ifname;
|
|
||||||
getInterfaceAddress();
|
|
||||||
}
|
|
||||||
dp.emit();
|
dp.emit();
|
||||||
|
// Ask for a dump of interfaces and then addresses to populate our
|
||||||
|
// information. First the interface dump, and once done, the callback
|
||||||
|
// will be called again which will ask for addresses dump.
|
||||||
|
askForStateDump();
|
||||||
worker();
|
worker();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,17 +169,29 @@ void waybar::modules::Network::createEventSocket() {
|
|||||||
ev_sock_ = nl_socket_alloc();
|
ev_sock_ = nl_socket_alloc();
|
||||||
nl_socket_disable_seq_check(ev_sock_);
|
nl_socket_disable_seq_check(ev_sock_);
|
||||||
nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this);
|
nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this);
|
||||||
|
nl_socket_modify_cb(ev_sock_, NL_CB_FINISH, NL_CB_CUSTOM, handleEventsDone, this);
|
||||||
auto groups = RTMGRP_LINK | (family_ == AF_INET ? RTMGRP_IPV4_IFADDR : RTMGRP_IPV6_IFADDR);
|
auto groups = RTMGRP_LINK | (family_ == AF_INET ? RTMGRP_IPV4_IFADDR : RTMGRP_IPV6_IFADDR);
|
||||||
nl_join_groups(ev_sock_, groups); // Deprecated
|
nl_join_groups(ev_sock_, groups); // Deprecated
|
||||||
if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) {
|
if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) {
|
||||||
throw std::runtime_error("Can't connect network socket");
|
throw std::runtime_error("Can't connect network socket");
|
||||||
}
|
}
|
||||||
|
if (nl_socket_set_nonblocking(ev_sock_)) {
|
||||||
|
throw std::runtime_error("Can't set non-blocking on network socket");
|
||||||
|
}
|
||||||
nl_socket_add_membership(ev_sock_, RTNLGRP_LINK);
|
nl_socket_add_membership(ev_sock_, RTNLGRP_LINK);
|
||||||
if (family_ == AF_INET) {
|
if (family_ == AF_INET) {
|
||||||
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
|
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
|
||||||
} else {
|
} else {
|
||||||
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
|
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
|
||||||
}
|
}
|
||||||
|
if (!config_["interface"].isString()) {
|
||||||
|
if (family_ == AF_INET) {
|
||||||
|
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_ROUTE);
|
||||||
|
} else {
|
||||||
|
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_ROUTE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
efd_ = epoll_create1(EPOLL_CLOEXEC);
|
efd_ = epoll_create1(EPOLL_CLOEXEC);
|
||||||
if (efd_ < 0) {
|
if (efd_ < 0) {
|
||||||
throw std::runtime_error("Can't create epoll");
|
throw std::runtime_error("Can't create epoll");
|
||||||
@ -212,18 +245,15 @@ void waybar::modules::Network::worker() {
|
|||||||
thread_timer_.sleep_for(interval_);
|
thread_timer_.sleep_for(interval_);
|
||||||
};
|
};
|
||||||
#ifdef WANT_RFKILL
|
#ifdef WANT_RFKILL
|
||||||
thread_rfkill_ = [this] {
|
rfkill_.on_update.connect([this](auto &) {
|
||||||
rfkill_.waitForEvent();
|
/* 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.
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
* Let's delegate the update to the timer thread instead of blocking the main thread.
|
||||||
if (ifid_ > 0) {
|
*/
|
||||||
getInfo();
|
thread_timer_.wake_up();
|
||||||
dp.emit();
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#else
|
#else
|
||||||
spdlog::warn("Waybar has been built without rfkill support.");
|
spdlog::warn("Waybar has been built without rfkill support.");
|
||||||
#endif
|
#endif
|
||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
std::array<struct epoll_event, EPOLL_MAX> events{};
|
std::array<struct epoll_event, EPOLL_MAX> events{};
|
||||||
@ -231,7 +261,23 @@ void waybar::modules::Network::worker() {
|
|||||||
int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1);
|
int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1);
|
||||||
if (ec > 0) {
|
if (ec > 0) {
|
||||||
for (auto i = 0; i < ec; i++) {
|
for (auto i = 0; i < ec; i++) {
|
||||||
if (events[i].data.fd != nl_socket_get_fd(ev_sock_) || nl_recvmsgs_default(ev_sock_) < 0) {
|
if (events[i].data.fd == nl_socket_get_fd(ev_sock_)) {
|
||||||
|
int rc = 0;
|
||||||
|
// Read as many message as possible, until the socket blocks
|
||||||
|
while (true) {
|
||||||
|
errno = 0;
|
||||||
|
rc = nl_recvmsgs_default(ev_sock_);
|
||||||
|
if (rc == -NLE_AGAIN || errno == EAGAIN) {
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rc < 0) {
|
||||||
|
spdlog::error("nl_recvmsgs_default error: {}", nl_geterror(-rc));
|
||||||
|
thread_.stop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
thread_.stop();
|
thread_.stop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -248,6 +294,7 @@ const std::string waybar::modules::Network::getNetworkState() const {
|
|||||||
#endif
|
#endif
|
||||||
return "disconnected";
|
return "disconnected";
|
||||||
}
|
}
|
||||||
|
if (!carrier_) 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";
|
||||||
@ -277,6 +324,10 @@ auto waybar::modules::Network::update() -> void {
|
|||||||
}
|
}
|
||||||
if (config_["format-" + state].isString()) {
|
if (config_["format-" + state].isString()) {
|
||||||
default_format_ = config_["format-" + state].asString();
|
default_format_ = config_["format-" + state].asString();
|
||||||
|
} else if (config_["format"].isString()) {
|
||||||
|
default_format_ = config_["format"].asString();
|
||||||
|
} else {
|
||||||
|
default_format_ = DEFAULT_FORMAT;
|
||||||
}
|
}
|
||||||
if (config_["tooltip-format-" + state].isString()) {
|
if (config_["tooltip-format-" + state].isString()) {
|
||||||
tooltip_format = config_["tooltip-format-" + state].asString();
|
tooltip_format = config_["tooltip-format-" + state].asString();
|
||||||
@ -345,349 +396,346 @@ auto waybar::modules::Network::update() -> void {
|
|||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
|
bool waybar::modules::Network::checkInterface(std::string name) {
|
||||||
int waybar::modules::Network::getExternalInterface(int skip_idx) const {
|
|
||||||
static const uint32_t route_buffer_size = 8192;
|
|
||||||
struct nlmsghdr * hdr = nullptr;
|
|
||||||
struct rtmsg * rt = nullptr;
|
|
||||||
char resp[route_buffer_size] = {0};
|
|
||||||
int ifidx = -1;
|
|
||||||
|
|
||||||
/* Prepare request. */
|
|
||||||
constexpr uint32_t reqlen = NLMSG_SPACE(sizeof(*rt));
|
|
||||||
char req[reqlen] = {0};
|
|
||||||
|
|
||||||
/* Build the RTM_GETROUTE request. */
|
|
||||||
hdr = reinterpret_cast<struct nlmsghdr *>(req);
|
|
||||||
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*rt));
|
|
||||||
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
|
||||||
hdr->nlmsg_type = RTM_GETROUTE;
|
|
||||||
rt = static_cast<struct rtmsg *>(NLMSG_DATA(hdr));
|
|
||||||
rt->rtm_family = family_;
|
|
||||||
rt->rtm_table = RT_TABLE_MAIN;
|
|
||||||
|
|
||||||
/* Issue the query. */
|
|
||||||
if (netlinkRequest(req, reqlen) < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read the response(s).
|
|
||||||
*
|
|
||||||
* WARNING: All the packets generated by the request must be consumed (as in,
|
|
||||||
* consume responses till NLMSG_DONE/NLMSG_ERROR is encountered).
|
|
||||||
*/
|
|
||||||
do {
|
|
||||||
auto len = netlinkResponse(resp, route_buffer_size);
|
|
||||||
if (len < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse the response payload into netlink messages. */
|
|
||||||
for (hdr = reinterpret_cast<struct nlmsghdr *>(resp); NLMSG_OK(hdr, len);
|
|
||||||
hdr = NLMSG_NEXT(hdr, len)) {
|
|
||||||
if (hdr->nlmsg_type == NLMSG_DONE) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (hdr->nlmsg_type == NLMSG_ERROR) {
|
|
||||||
/* Even if we found the interface index, something is broken with the
|
|
||||||
* netlink socket, so return an error.
|
|
||||||
*/
|
|
||||||
ifidx = -1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we found the correct answer, skip parsing the attributes. */
|
|
||||||
if (ifidx != -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find the message(s) concerting the main routing table, each message
|
|
||||||
* corresponds to a single routing table entry.
|
|
||||||
*/
|
|
||||||
rt = static_cast<struct rtmsg *>(NLMSG_DATA(hdr));
|
|
||||||
if (rt->rtm_table != RT_TABLE_MAIN) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse all the attributes for a single routing table entry. */
|
|
||||||
struct rtattr *attr = RTM_RTA(rt);
|
|
||||||
uint64_t attrlen = RTM_PAYLOAD(hdr);
|
|
||||||
bool has_gateway = false;
|
|
||||||
bool has_destination = false;
|
|
||||||
int temp_idx = -1;
|
|
||||||
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) {
|
|
||||||
/* Determine if this routing table entry corresponds to the default
|
|
||||||
* route by seeing if it has a gateway, and if a destination addr is
|
|
||||||
* set, that it is all 0s.
|
|
||||||
*/
|
|
||||||
switch (attr->rta_type) {
|
|
||||||
case RTA_GATEWAY:
|
|
||||||
/* The gateway of the route.
|
|
||||||
*
|
|
||||||
* If someone every needs to figure out the gateway address as well,
|
|
||||||
* it's here as the attribute payload.
|
|
||||||
*/
|
|
||||||
has_gateway = true;
|
|
||||||
break;
|
|
||||||
case RTA_DST: {
|
|
||||||
/* The destination address.
|
|
||||||
* Should be either missing, or maybe all 0s. Accept both.
|
|
||||||
*/
|
|
||||||
const uint32_t nr_zeroes = (family_ == AF_INET) ? 4 : 16;
|
|
||||||
unsigned char c = 0;
|
|
||||||
size_t dstlen = RTA_PAYLOAD(attr);
|
|
||||||
if (dstlen != nr_zeroes) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (uint32_t i = 0; i < dstlen; i += 1) {
|
|
||||||
c |= *((unsigned char *)RTA_DATA(attr) + i);
|
|
||||||
}
|
|
||||||
has_destination = (c == 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RTA_OIF:
|
|
||||||
/* The output interface index. */
|
|
||||||
temp_idx = *static_cast<int *>(RTA_DATA(attr));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* If this is the default route, and we know the interface index,
|
|
||||||
* we can stop parsing this message.
|
|
||||||
*/
|
|
||||||
if (has_gateway && !has_destination && temp_idx != -1 && temp_idx != skip_idx) {
|
|
||||||
ifidx = temp_idx;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return ifidx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::modules::Network::getInterfaceAddress() {
|
|
||||||
struct ifaddrs *ifaddr, *ifa;
|
|
||||||
cidr_ = 0;
|
|
||||||
int success = getifaddrs(&ifaddr);
|
|
||||||
if (success != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ifa = ifaddr;
|
|
||||||
while (ifa != nullptr) {
|
|
||||||
if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ &&
|
|
||||||
ifa->ifa_name == ifname_) {
|
|
||||||
char ipaddr[INET6_ADDRSTRLEN];
|
|
||||||
char netmask[INET6_ADDRSTRLEN];
|
|
||||||
unsigned int cidr = 0;
|
|
||||||
if (family_ == AF_INET) {
|
|
||||||
ipaddr_ = inet_ntop(AF_INET,
|
|
||||||
&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
|
|
||||||
ipaddr,
|
|
||||||
INET_ADDRSTRLEN);
|
|
||||||
auto net_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask);
|
|
||||||
netmask_ = inet_ntop(AF_INET, &net_addr->sin_addr, netmask, INET_ADDRSTRLEN);
|
|
||||||
unsigned int cidrRaw = net_addr->sin_addr.s_addr;
|
|
||||||
while (cidrRaw) {
|
|
||||||
cidr += cidrRaw & 1;
|
|
||||||
cidrRaw >>= 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ipaddr_ = inet_ntop(AF_INET6,
|
|
||||||
&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr,
|
|
||||||
ipaddr,
|
|
||||||
INET6_ADDRSTRLEN);
|
|
||||||
auto net_addr = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_netmask);
|
|
||||||
netmask_ = inet_ntop(AF_INET6, &net_addr->sin6_addr, netmask, INET6_ADDRSTRLEN);
|
|
||||||
for (size_t i = 0; i < sizeof(net_addr->sin6_addr.s6_addr); ++i) {
|
|
||||||
unsigned char cidrRaw = net_addr->sin6_addr.s6_addr[i];
|
|
||||||
while (cidrRaw) {
|
|
||||||
cidr += cidrRaw & 1;
|
|
||||||
cidrRaw >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cidr_ = cidr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ifa = ifa->ifa_next;
|
|
||||||
}
|
|
||||||
freeifaddrs(ifaddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) const {
|
|
||||||
struct sockaddr_nl sa = {};
|
|
||||||
sa.nl_family = AF_NETLINK;
|
|
||||||
sa.nl_groups = groups;
|
|
||||||
struct iovec iov = {req, reqlen};
|
|
||||||
struct msghdr msg = {
|
|
||||||
.msg_name = &sa,
|
|
||||||
.msg_namelen = sizeof(sa),
|
|
||||||
.msg_iov = &iov,
|
|
||||||
.msg_iovlen = 1,
|
|
||||||
};
|
|
||||||
return sendmsg(nl_socket_get_fd(ev_sock_), &msg, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint32_t groups) const {
|
|
||||||
struct sockaddr_nl sa = {};
|
|
||||||
sa.nl_family = AF_NETLINK;
|
|
||||||
sa.nl_groups = groups;
|
|
||||||
struct iovec iov = {resp, resplen};
|
|
||||||
struct msghdr msg = {
|
|
||||||
.msg_name = &sa,
|
|
||||||
.msg_namelen = sizeof(sa),
|
|
||||||
.msg_iov = &iov,
|
|
||||||
.msg_iovlen = 1,
|
|
||||||
};
|
|
||||||
auto ret = recvmsg(nl_socket_get_fd(ev_sock_), &msg, 0);
|
|
||||||
if (msg.msg_flags & MSG_TRUNC) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::string name) {
|
|
||||||
if (config_["interface"].isString()) {
|
if (config_["interface"].isString()) {
|
||||||
return config_["interface"].asString() == name ||
|
return config_["interface"].asString() == name ||
|
||||||
wildcardMatch(config_["interface"].asString(), name);
|
wildcardMatch(config_["interface"].asString(), name);
|
||||||
}
|
}
|
||||||
// getExternalInterface may need some delay to detect external interface
|
|
||||||
for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) {
|
|
||||||
auto external_iface = getExternalInterface();
|
|
||||||
if (external_iface > 0) {
|
|
||||||
return external_iface == rtif->ifi_index;
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int waybar::modules::Network::getPreferredIface(int skip_idx, bool wait) const {
|
|
||||||
int ifid = -1;
|
|
||||||
if (config_["interface"].isString()) {
|
|
||||||
ifid = if_nametoindex(config_["interface"].asCString());
|
|
||||||
if (ifid > 0) {
|
|
||||||
return ifid;
|
|
||||||
} else {
|
|
||||||
// Try with wildcard
|
|
||||||
struct ifaddrs *ifaddr, *ifa;
|
|
||||||
int success = getifaddrs(&ifaddr);
|
|
||||||
if (success != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
ifa = ifaddr;
|
|
||||||
ifid = -1;
|
|
||||||
while (ifa != nullptr) {
|
|
||||||
if (wildcardMatch(config_["interface"].asString(), ifa->ifa_name)) {
|
|
||||||
ifid = if_nametoindex(ifa->ifa_name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ifa = ifa->ifa_next;
|
|
||||||
}
|
|
||||||
freeifaddrs(ifaddr);
|
|
||||||
return ifid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// getExternalInterface may need some delay to detect external interface
|
|
||||||
for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) {
|
|
||||||
ifid = getExternalInterface(skip_idx);
|
|
||||||
if (ifid > 0) {
|
|
||||||
return ifid;
|
|
||||||
}
|
|
||||||
if (wait) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::modules::Network::clearIface() {
|
void waybar::modules::Network::clearIface() {
|
||||||
|
ifid_ = -1;
|
||||||
|
ifname_.clear();
|
||||||
essid_.clear();
|
essid_.clear();
|
||||||
ipaddr_.clear();
|
ipaddr_.clear();
|
||||||
netmask_.clear();
|
netmask_.clear();
|
||||||
|
carrier_ = false;
|
||||||
cidr_ = 0;
|
cidr_ = 0;
|
||||||
signal_strength_dbm_ = 0;
|
signal_strength_dbm_ = 0;
|
||||||
signal_strength_ = 0;
|
signal_strength_ = 0;
|
||||||
frequency_ = 0;
|
frequency_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Network::checkNewInterface(struct ifinfomsg *rtif) {
|
|
||||||
auto new_iface = getPreferredIface(rtif->ifi_index);
|
|
||||||
if (new_iface != -1) {
|
|
||||||
ifid_ = new_iface;
|
|
||||||
char ifname[IF_NAMESIZE];
|
|
||||||
if_indextoname(new_iface, ifname);
|
|
||||||
ifname_ = ifname;
|
|
||||||
getInterfaceAddress();
|
|
||||||
thread_timer_.wake_up();
|
|
||||||
} else {
|
|
||||||
ifid_ = -1;
|
|
||||||
dp.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
||||||
auto net = static_cast<waybar::modules::Network *>(data);
|
auto net = static_cast<waybar::modules::Network *>(data);
|
||||||
std::lock_guard<std::mutex> lock(net->mutex_);
|
std::lock_guard<std::mutex> lock(net->mutex_);
|
||||||
auto nh = nlmsg_hdr(msg);
|
auto nh = nlmsg_hdr(msg);
|
||||||
auto ifi = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
|
bool is_del_event = false;
|
||||||
if (nh->nlmsg_type == RTM_DELADDR) {
|
|
||||||
// Check for valid interface
|
switch (nh->nlmsg_type) {
|
||||||
if (ifi->ifi_index == net->ifid_) {
|
case RTM_DELLINK:
|
||||||
net->ipaddr_.clear();
|
is_del_event = true;
|
||||||
net->netmask_.clear();
|
case RTM_NEWLINK: {
|
||||||
net->cidr_ = 0;
|
struct ifinfomsg *ifi = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
|
||||||
if (!(ifi->ifi_flags & IFF_RUNNING)) {
|
ssize_t attrlen = IFLA_PAYLOAD(nh);
|
||||||
net->clearIface();
|
struct rtattr *ifla = IFLA_RTA(ifi);
|
||||||
// Check for a new interface and get info
|
const char *ifname = NULL;
|
||||||
net->checkNewInterface(ifi);
|
size_t ifname_len = 0;
|
||||||
} else {
|
std::optional<bool> carrier;
|
||||||
net->dp.emit();
|
|
||||||
}
|
if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) {
|
||||||
return NL_OK;
|
return NL_OK;
|
||||||
}
|
}
|
||||||
} else if (nh->nlmsg_type == RTM_NEWLINK || nh->nlmsg_type == RTM_DELLINK) {
|
|
||||||
char ifname[IF_NAMESIZE];
|
// Check if the interface goes "down" and if we want to detect the
|
||||||
if_indextoname(ifi->ifi_index, ifname);
|
// external interface.
|
||||||
// Check for valid interface
|
if (net->ifid_ != -1 && !(ifi->ifi_flags & IFF_UP)
|
||||||
if (ifi->ifi_index != net->ifid_ && net->checkInterface(ifi, ifname)) {
|
&& !net->config_["interface"].isString()) {
|
||||||
net->ifname_ = ifname;
|
// The current interface is now down, all the routes associated with
|
||||||
net->ifid_ = ifi->ifi_index;
|
// it have been deleted, so start looking for a new default route.
|
||||||
// Get Iface and WIFI info
|
spdlog::debug("network: if{} down", net->ifid_);
|
||||||
net->getInterfaceAddress();
|
|
||||||
net->thread_timer_.wake_up();
|
|
||||||
return NL_OK;
|
|
||||||
} else if (ifi->ifi_index == net->ifid_ &&
|
|
||||||
(!(ifi->ifi_flags & IFF_RUNNING) || !(ifi->ifi_flags & IFF_UP) ||
|
|
||||||
!net->checkInterface(ifi, ifname))) {
|
|
||||||
net->clearIface();
|
net->clearIface();
|
||||||
// Check for a new interface and get info
|
net->dp.emit();
|
||||||
net->checkNewInterface(ifi);
|
net->want_route_dump_ = true;
|
||||||
|
net->askForStateDump();
|
||||||
return NL_OK;
|
return NL_OK;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
char ifname[IF_NAMESIZE];
|
for (; RTA_OK(ifla, attrlen); ifla = RTA_NEXT(ifla, attrlen)) {
|
||||||
if_indextoname(ifi->ifi_index, ifname);
|
switch (ifla->rta_type) {
|
||||||
// Auto detected network can also be assigned here
|
case IFLA_IFNAME:
|
||||||
if (ifi->ifi_index != net->ifid_ && net->checkInterface(ifi, ifname)) {
|
ifname = static_cast<const char *>(RTA_DATA(ifla));
|
||||||
// If iface is different, clear data
|
ifname_len = RTA_PAYLOAD(ifla) - 1; // minus \0
|
||||||
if (ifi->ifi_index != net->ifid_) {
|
break;
|
||||||
net->clearIface();
|
case IFLA_CARRIER: {
|
||||||
|
carrier = *(char*)RTA_DATA(ifla) == 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
net->ifname_ = ifname;
|
|
||||||
net->ifid_ = ifi->ifi_index;
|
|
||||||
}
|
}
|
||||||
// Check for valid interface
|
|
||||||
if (ifi->ifi_index == net->ifid_) {
|
if (!is_del_event && ifi->ifi_index == net->ifid_) {
|
||||||
// Get Iface and WIFI info
|
// Update inferface information
|
||||||
net->getInterfaceAddress();
|
if (net->ifname_.empty() && ifname != NULL) {
|
||||||
net->thread_timer_.wake_up();
|
std::string new_ifname (ifname, ifname_len);
|
||||||
|
net->ifname_ = new_ifname;
|
||||||
|
}
|
||||||
|
if (carrier.has_value()) {
|
||||||
|
if (net->carrier_ != *carrier) {
|
||||||
|
if (*carrier) {
|
||||||
|
// Ask for WiFi information
|
||||||
|
net->thread_timer_.wake_up();
|
||||||
|
} else {
|
||||||
|
// clear state related to WiFi connection
|
||||||
|
net->essid_.clear();
|
||||||
|
net->signal_strength_dbm_ = 0;
|
||||||
|
net->signal_strength_ = 0;
|
||||||
|
net->frequency_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
net->carrier_ = carrier.value();
|
||||||
|
}
|
||||||
|
} else if (!is_del_event && net->ifid_ == -1) {
|
||||||
|
// Checking if it's an interface we care about.
|
||||||
|
std::string new_ifname (ifname, ifname_len);
|
||||||
|
if (net->checkInterface(new_ifname)) {
|
||||||
|
spdlog::debug("network: selecting new interface {}/{}", new_ifname, ifi->ifi_index);
|
||||||
|
|
||||||
|
net->ifname_ = new_ifname;
|
||||||
|
net->ifid_ = ifi->ifi_index;
|
||||||
|
if (carrier.has_value()) {
|
||||||
|
net->carrier_ = carrier.value();
|
||||||
|
}
|
||||||
|
net->thread_timer_.wake_up();
|
||||||
|
/* An address for this new interface should be received via an
|
||||||
|
* RTM_NEWADDR event either because we ask for a dump of both links
|
||||||
|
* and addrs, or because this interface has just been created and
|
||||||
|
* the addr will be sent after the RTM_NEWLINK event.
|
||||||
|
* So we don't need to do anything. */
|
||||||
|
}
|
||||||
|
} else if (is_del_event && net->ifid_ >= 0) {
|
||||||
|
// Our interface has been deleted, start looking/waiting for one we care.
|
||||||
|
spdlog::debug("network: interface {}/{} deleted", net->ifname_, net->ifid_);
|
||||||
|
|
||||||
|
net->clearIface();
|
||||||
|
net->dp.emit();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RTM_DELADDR:
|
||||||
|
is_del_event = true;
|
||||||
|
case RTM_NEWADDR: {
|
||||||
|
struct ifaddrmsg *ifa = static_cast<struct ifaddrmsg *>(NLMSG_DATA(nh));
|
||||||
|
ssize_t attrlen = IFA_PAYLOAD(nh);
|
||||||
|
struct rtattr *ifa_rta = IFA_RTA(ifa);
|
||||||
|
|
||||||
|
if ((int)ifa->ifa_index != net->ifid_) {
|
||||||
return NL_OK;
|
return NL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ifa->ifa_family != net->family_) {
|
||||||
|
return NL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We ignore address mark as scope for the link or host,
|
||||||
|
// which should leave scope global addresses.
|
||||||
|
if (ifa->ifa_scope >= RT_SCOPE_LINK) {
|
||||||
|
return NL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; RTA_OK(ifa_rta, attrlen); ifa_rta = RTA_NEXT(ifa_rta, attrlen)) {
|
||||||
|
switch (ifa_rta->rta_type) {
|
||||||
|
case IFA_ADDRESS: {
|
||||||
|
char ipaddr[INET6_ADDRSTRLEN];
|
||||||
|
if (!is_del_event) {
|
||||||
|
net->ipaddr_ = inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta),
|
||||||
|
ipaddr, sizeof (ipaddr));
|
||||||
|
net->cidr_ = ifa->ifa_prefixlen;
|
||||||
|
switch (ifa->ifa_family) {
|
||||||
|
case AF_INET: {
|
||||||
|
struct in_addr netmask;
|
||||||
|
netmask.s_addr = htonl(~0 << (32 - ifa->ifa_prefixlen));
|
||||||
|
net->netmask_ = inet_ntop(ifa->ifa_family, &netmask,
|
||||||
|
ipaddr, sizeof (ipaddr));
|
||||||
|
}
|
||||||
|
case AF_INET6: {
|
||||||
|
struct in6_addr netmask;
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
int v = (i + 1) * 8 - ifa->ifa_prefixlen;
|
||||||
|
if (v < 0) v = 0;
|
||||||
|
if (v > 8) v = 8;
|
||||||
|
netmask.s6_addr[i] = ~0 << v;
|
||||||
|
}
|
||||||
|
net->netmask_ = inet_ntop(ifa->ifa_family, &netmask,
|
||||||
|
ipaddr, sizeof (ipaddr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spdlog::debug("network: {}, new addr {}/{}", net->ifname_, net->ipaddr_, net->cidr_);
|
||||||
|
} else {
|
||||||
|
net->ipaddr_.clear();
|
||||||
|
net->cidr_ = 0;
|
||||||
|
net->netmask_.clear();
|
||||||
|
spdlog::debug("network: {} addr deleted {}/{}",
|
||||||
|
net->ifname_,
|
||||||
|
inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta),
|
||||||
|
ipaddr, sizeof (ipaddr)),
|
||||||
|
ifa->ifa_prefixlen);
|
||||||
|
}
|
||||||
|
net->dp.emit();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return NL_SKIP;
|
|
||||||
|
case RTM_DELROUTE:
|
||||||
|
is_del_event = true;
|
||||||
|
case RTM_NEWROUTE: {
|
||||||
|
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
|
||||||
|
// to find the interface used to reach the outside world
|
||||||
|
|
||||||
|
struct rtmsg *rtm = static_cast<struct rtmsg *>(NLMSG_DATA(nh));
|
||||||
|
ssize_t attrlen = RTM_PAYLOAD(nh);
|
||||||
|
struct rtattr *attr = RTM_RTA(rtm);
|
||||||
|
bool has_gateway = false;
|
||||||
|
bool has_destination = false;
|
||||||
|
int temp_idx = -1;
|
||||||
|
uint32_t priority = 0;
|
||||||
|
|
||||||
|
/* Find the message(s) concerting the main routing table, each message
|
||||||
|
* corresponds to a single routing table entry.
|
||||||
|
*/
|
||||||
|
if (rtm->rtm_table != RT_TABLE_MAIN) {
|
||||||
|
return NL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse all the attributes for a single routing table entry. */
|
||||||
|
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) {
|
||||||
|
/* Determine if this routing table entry corresponds to the default
|
||||||
|
* route by seeing if it has a gateway, and if a destination addr is
|
||||||
|
* set, that it is all 0s.
|
||||||
|
*/
|
||||||
|
switch(attr->rta_type) {
|
||||||
|
case RTA_GATEWAY:
|
||||||
|
/* The gateway of the route.
|
||||||
|
*
|
||||||
|
* If someone every needs to figure out the gateway address as well,
|
||||||
|
* it's here as the attribute payload.
|
||||||
|
*/
|
||||||
|
has_gateway = true;
|
||||||
|
break;
|
||||||
|
case RTA_DST: {
|
||||||
|
/* The destination address.
|
||||||
|
* Should be either missing, or maybe all 0s. Accept both.
|
||||||
|
*/
|
||||||
|
const uint32_t nr_zeroes = (net->family_ == AF_INET) ? 4 : 16;
|
||||||
|
unsigned char c = 0;
|
||||||
|
size_t dstlen = RTA_PAYLOAD(attr);
|
||||||
|
if (dstlen != nr_zeroes) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < dstlen; i += 1) {
|
||||||
|
c |= *((unsigned char *)RTA_DATA(attr) + i);
|
||||||
|
}
|
||||||
|
has_destination = (c == 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RTA_OIF:
|
||||||
|
/* The output interface index. */
|
||||||
|
temp_idx = *static_cast<int *>(RTA_DATA(attr));
|
||||||
|
break;
|
||||||
|
case RTA_PRIORITY:
|
||||||
|
priority = *(uint32_t*)RTA_DATA(attr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have a default route.
|
||||||
|
if (has_gateway && !has_destination && temp_idx != -1) {
|
||||||
|
// Check if this is the first default route we see, or if this new
|
||||||
|
// route have a higher priority.
|
||||||
|
if (!is_del_event && ((net->ifid_ == -1) || (priority < net->route_priority))) {
|
||||||
|
// Clear if's state for the case were there is a higher priority
|
||||||
|
// route on a different interface.
|
||||||
|
net->clearIface();
|
||||||
|
net->ifid_ = temp_idx;
|
||||||
|
net->route_priority = priority;
|
||||||
|
|
||||||
|
spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority);
|
||||||
|
|
||||||
|
/* Ask ifname associated with temp_idx as well as carrier status */
|
||||||
|
struct ifinfomsg ifinfo_hdr = {
|
||||||
|
.ifi_family = AF_UNSPEC,
|
||||||
|
.ifi_index = temp_idx,
|
||||||
|
};
|
||||||
|
int err;
|
||||||
|
err = nl_send_simple(net->ev_sock_, RTM_GETLINK, NLM_F_REQUEST,
|
||||||
|
&ifinfo_hdr, sizeof (ifinfo_hdr));
|
||||||
|
if (err < 0) {
|
||||||
|
spdlog::error("network: failed to ask link info: {}", err);
|
||||||
|
/* Ask for a dump of all links instead */
|
||||||
|
net->want_link_dump_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Also ask for the address. Asking for a addresses of a specific
|
||||||
|
* interface doesn't seems to work so ask for a dump of all
|
||||||
|
* addresses. */
|
||||||
|
net->want_addr_dump_ = true;
|
||||||
|
net->askForStateDump();
|
||||||
|
net->thread_timer_.wake_up();
|
||||||
|
} else if (is_del_event && temp_idx == net->ifid_
|
||||||
|
&& net->route_priority == priority) {
|
||||||
|
spdlog::debug("network: default route deleted {}/if{} metric {}",
|
||||||
|
net->ifname_, temp_idx, priority);
|
||||||
|
|
||||||
|
net->clearIface();
|
||||||
|
net->dp.emit();
|
||||||
|
/* Ask for a dump of all routes in case another one is already
|
||||||
|
* setup. If there's none, there'll be an event with new one
|
||||||
|
* later. */
|
||||||
|
net->want_route_dump_ = true;
|
||||||
|
net->askForStateDump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::modules::Network::askForStateDump(void) {
|
||||||
|
/* We need to wait until the current dump is done before sending new
|
||||||
|
* messages. handleEventsDone() is called when a dump is done. */
|
||||||
|
if (dump_in_progress_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct rtgenmsg rt_hdr = {
|
||||||
|
.rtgen_family = AF_UNSPEC,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (want_route_dump_) {
|
||||||
|
rt_hdr.rtgen_family = family_;
|
||||||
|
nl_send_simple(ev_sock_, RTM_GETROUTE, NLM_F_DUMP,
|
||||||
|
&rt_hdr, sizeof (rt_hdr));
|
||||||
|
want_route_dump_ = false;
|
||||||
|
dump_in_progress_ = true;
|
||||||
|
|
||||||
|
} else if (want_link_dump_) {
|
||||||
|
nl_send_simple(ev_sock_, RTM_GETLINK, NLM_F_DUMP,
|
||||||
|
&rt_hdr, sizeof (rt_hdr));
|
||||||
|
want_link_dump_ = false;
|
||||||
|
dump_in_progress_ = true;
|
||||||
|
|
||||||
|
} else if (want_addr_dump_) {
|
||||||
|
rt_hdr.rtgen_family = family_;
|
||||||
|
nl_send_simple(ev_sock_, RTM_GETADDR, NLM_F_DUMP,
|
||||||
|
&rt_hdr, sizeof (rt_hdr));
|
||||||
|
want_addr_dump_ = false;
|
||||||
|
dump_in_progress_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int waybar::modules::Network::handleEventsDone(struct nl_msg *msg, void *data) {
|
||||||
|
auto net = static_cast<waybar::modules::Network *>(data);
|
||||||
|
net->dump_in_progress_ = false;
|
||||||
|
net->askForStateDump();
|
||||||
|
return NL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
|
int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {
|
||||||
|
@ -151,8 +151,24 @@ void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const p
|
|||||||
*/
|
*/
|
||||||
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)
|
||||||
|
return;
|
||||||
|
|
||||||
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) {
|
if (pa->current_sink_name_ == i->name) {
|
||||||
|
if (i->state != PA_SINK_RUNNING) {
|
||||||
|
pa->current_sink_running_ = false;
|
||||||
|
} else {
|
||||||
|
pa->current_sink_running_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pa->current_sink_running_ && i->state == PA_SINK_RUNNING) {
|
||||||
|
pa->current_sink_name_ = i->name;
|
||||||
|
pa->current_sink_running_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pa->current_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;
|
||||||
@ -175,11 +191,11 @@ 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);
|
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
||||||
pa->default_sink_name_ = i->default_sink_name;
|
pa->current_sink_name_ = i->default_sink_name;
|
||||||
pa->default_source_name_ = i->default_source_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_list(context, sinkInfoCb, data);
|
||||||
pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data);
|
pa_context_get_source_info_list(context, sourceInfoCb, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::array<std::string, 9> ports = {
|
static const std::array<std::string, 9> ports = {
|
||||||
@ -194,22 +210,26 @@ static const std::array<std::string, 9> ports = {
|
|||||||
"phone",
|
"phone",
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string waybar::modules::Pulseaudio::getPortIcon() const {
|
const std::vector<std::string> waybar::modules::Pulseaudio::getPulseIcon() const {
|
||||||
|
std::vector<std::string> res = {default_source_name_};
|
||||||
std::string nameLC = port_name_ + form_factor_;
|
std::string nameLC = port_name_ + form_factor_;
|
||||||
std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower);
|
std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower);
|
||||||
for (auto const &port : ports) {
|
for (auto const &port : ports) {
|
||||||
if (nameLC.find(port) != std::string::npos) {
|
if (nameLC.find(port) != std::string::npos) {
|
||||||
return port;
|
res.push_back(port);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return port_name_;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Pulseaudio::update() -> void {
|
auto waybar::modules::Pulseaudio::update() -> void {
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
|
std::string tooltip_format;
|
||||||
if (!alt_) {
|
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 || // PulseAudio
|
||||||
|
monitor_.find("a2dp-sink") != std::string::npos) { // PipeWire
|
||||||
format_name = format_name + "-bluetooth";
|
format_name = format_name + "-bluetooth";
|
||||||
label_.get_style_context()->add_class("bluetooth");
|
label_.get_style_context()->add_class("bluetooth");
|
||||||
} else {
|
} else {
|
||||||
@ -222,28 +242,53 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
|||||||
}
|
}
|
||||||
format_name = format_name + "-muted";
|
format_name = format_name + "-muted";
|
||||||
label_.get_style_context()->add_class("muted");
|
label_.get_style_context()->add_class("muted");
|
||||||
|
label_.get_style_context()->add_class("sink-muted");
|
||||||
} else {
|
} else {
|
||||||
label_.get_style_context()->remove_class("muted");
|
label_.get_style_context()->remove_class("muted");
|
||||||
|
label_.get_style_context()->remove_class("sink-muted");
|
||||||
}
|
}
|
||||||
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_) {
|
||||||
format_source = config_["format-source-muted"].asString();
|
label_.get_style_context()->add_class("source-muted");
|
||||||
} else if (!source_muted_ && config_["format-source"].isString()) {
|
if (config_["format-source-muted"].isString()) {
|
||||||
format_source = config_["format-source"].asString();
|
format_source = config_["format-source-muted"].asString();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
label_.get_style_context()->remove_class("source-muted");
|
||||||
|
if (config_["format-source-muted"].isString()) {
|
||||||
|
format_source = config_["format-source"].asString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
format_source = fmt::format(format_source, fmt::arg("volume", source_volume_));
|
format_source = fmt::format(format_source, fmt::arg("volume", source_volume_));
|
||||||
label_.set_markup(fmt::format(format,
|
label_.set_markup(fmt::format(format,
|
||||||
fmt::arg("desc", desc_),
|
fmt::arg("desc", desc_),
|
||||||
fmt::arg("volume", volume_),
|
fmt::arg("volume", volume_),
|
||||||
fmt::arg("format_source", format_source),
|
fmt::arg("format_source", format_source),
|
||||||
fmt::arg("icon", getIcon(volume_, getPortIcon()))));
|
fmt::arg("source_volume", source_volume_),
|
||||||
|
fmt::arg("source_desc", source_desc_),
|
||||||
|
fmt::arg("icon", getIcon(volume_, getPulseIcon()))));
|
||||||
getState(volume_);
|
getState(volume_);
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
label_.set_tooltip_text(desc_);
|
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
}
|
||||||
|
if (!tooltip_format.empty()) {
|
||||||
|
label_.set_tooltip_text(fmt::format(
|
||||||
|
tooltip_format,
|
||||||
|
fmt::arg("desc", desc_),
|
||||||
|
fmt::arg("volume", volume_),
|
||||||
|
fmt::arg("format_source", format_source),
|
||||||
|
fmt::arg("source_volume", source_volume_),
|
||||||
|
fmt::arg("source_desc", source_desc_),
|
||||||
|
fmt::arg("icon", getIcon(volume_, getPulseIcon()))));
|
||||||
|
} else {
|
||||||
|
label_.set_tooltip_text(desc_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call parent update
|
// Call parent update
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
#include "modules/river/tags.hpp"
|
#include "modules/river/tags.hpp"
|
||||||
#include "river-status-unstable-v1-client-protocol.h"
|
#include "river-status-unstable-v1-client-protocol.h"
|
||||||
@ -64,8 +66,20 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con
|
|||||||
|
|
||||||
// Default to 9 tags
|
// Default to 9 tags
|
||||||
const uint32_t num_tags = config["num-tags"].isUInt() ? config_["num-tags"].asUInt() : 9;
|
const uint32_t num_tags = config["num-tags"].isUInt() ? config_["num-tags"].asUInt() : 9;
|
||||||
for (uint32_t tag = 1; tag <= num_tags; ++tag) {
|
|
||||||
Gtk::Button &button = buttons_.emplace_back(std::to_string(tag));
|
std::vector<std::string> tag_labels(num_tags);
|
||||||
|
for (uint32_t tag = 0; tag < num_tags; ++tag) {
|
||||||
|
tag_labels[tag] = std::to_string(tag+1);
|
||||||
|
}
|
||||||
|
const Json::Value custom_labels = config["tag-labels"];
|
||||||
|
if (custom_labels.isArray() && !custom_labels.empty()) {
|
||||||
|
for (uint32_t tag = 0; tag < std::min(num_tags, custom_labels.size()); ++tag) {
|
||||||
|
tag_labels[tag] = custom_labels[tag].asString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &tag_label : tag_labels) {
|
||||||
|
Gtk::Button &button = buttons_.emplace_back(tag_label);
|
||||||
button.set_relief(Gtk::RELIEF_NONE);
|
button.set_relief(Gtk::RELIEF_NONE);
|
||||||
box_.pack_start(button, false, false, 0);
|
box_.pack_start(button, false, false, 0);
|
||||||
button.show();
|
button.show();
|
||||||
|
33
src/modules/simpleclock.cpp
Normal file
33
src/modules/simpleclock.cpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#include "modules/simpleclock.hpp"
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||||
|
: ALabel(config, "clock", id, "{:%H:%M}", 60) {
|
||||||
|
thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto timeout = std::chrono::floor<std::chrono::seconds>(now + interval_);
|
||||||
|
auto diff = std::chrono::seconds(timeout.time_since_epoch().count() % interval_.count());
|
||||||
|
thread_.sleep_until(timeout - diff);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Clock::update() -> void {
|
||||||
|
tzset(); // Update timezone information
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
|
||||||
|
auto text = fmt::format(format_, localtime);
|
||||||
|
label_.set_markup(text);
|
||||||
|
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
if (config_["tooltip-format"].isString()) {
|
||||||
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
auto tooltip_text = fmt::format(tooltip_format, localtime);
|
||||||
|
label_.set_tooltip_text(tooltip_text);
|
||||||
|
} else {
|
||||||
|
label_.set_tooltip_text(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
|
}
|
@ -1,7 +1,12 @@
|
|||||||
#include "modules/sni/item.hpp"
|
#include "modules/sni/item.hpp"
|
||||||
|
|
||||||
|
#include <gdkmm/general.h>
|
||||||
#include <glibmm/main.h>
|
#include <glibmm/main.h>
|
||||||
|
#include <gtkmm/tooltip.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct fmt::formatter<Glib::ustring> : formatter<std::string> {
|
struct fmt::formatter<Glib::ustring> : formatter<std::string> {
|
||||||
@ -39,14 +44,22 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf
|
|||||||
object_path(op),
|
object_path(op),
|
||||||
icon_size(16),
|
icon_size(16),
|
||||||
effective_icon_size(0),
|
effective_icon_size(0),
|
||||||
icon_theme(Gtk::IconTheme::create()),
|
icon_theme(Gtk::IconTheme::create()) {
|
||||||
update_pending_(false) {
|
|
||||||
if (config["icon-size"].isUInt()) {
|
if (config["icon-size"].isUInt()) {
|
||||||
icon_size = config["icon-size"].asUInt();
|
icon_size = config["icon-size"].asUInt();
|
||||||
}
|
}
|
||||||
|
if (config["smooth-scrolling-threshold"].isNumeric()) {
|
||||||
|
scroll_threshold_ = config["smooth-scrolling-threshold"].asDouble();
|
||||||
|
}
|
||||||
|
if (config["show-passive-items"].isBool()) {
|
||||||
|
show_passive_ = config["show-passive-items"].asBool();
|
||||||
|
}
|
||||||
event_box.add(image);
|
event_box.add(image);
|
||||||
event_box.add_events(Gdk::BUTTON_PRESS_MASK);
|
event_box.add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
|
||||||
event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick));
|
event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick));
|
||||||
|
event_box.signal_scroll_event().connect(sigc::mem_fun(*this, &Item::handleScroll));
|
||||||
|
// initial visibility
|
||||||
|
event_box.set_visible(show_passive_);
|
||||||
|
|
||||||
cancellable_ = Gio::Cancellable::create();
|
cancellable_ = Gio::Cancellable::create();
|
||||||
|
|
||||||
@ -73,12 +86,11 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
|
|||||||
|
|
||||||
this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal));
|
this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal));
|
||||||
|
|
||||||
if (this->id.empty() || this->category.empty() || this->status.empty()) {
|
if (this->id.empty() || this->category.empty()) {
|
||||||
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
|
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->updateImage();
|
this->updateImage();
|
||||||
// this->event_box.set_tooltip_text(this->title);
|
|
||||||
|
|
||||||
} catch (const Glib::Error& err) {
|
} catch (const Glib::Error& err) {
|
||||||
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
|
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
|
||||||
@ -88,10 +100,24 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get_variant(Glib::VariantBase& value) {
|
T get_variant(const Glib::VariantBase& value) {
|
||||||
return Glib::VariantBase::cast_dynamic<Glib::Variant<T>>(value).get();
|
return Glib::VariantBase::cast_dynamic<Glib::Variant<T>>(value).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
ToolTip get_variant<ToolTip>(const Glib::VariantBase& value) {
|
||||||
|
ToolTip result;
|
||||||
|
// Unwrap (sa(iiay)ss)
|
||||||
|
auto container = value.cast_dynamic<Glib::VariantContainerBase>(value);
|
||||||
|
result.icon_name = get_variant<Glib::ustring>(container.get_child(0));
|
||||||
|
result.text = get_variant<Glib::ustring>(container.get_child(2));
|
||||||
|
auto description = get_variant<Glib::ustring>(container.get_child(3));
|
||||||
|
if (!description.empty()) {
|
||||||
|
result.text = fmt::format("<b>{}</b>\n{}", result.text, description);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
||||||
try {
|
try {
|
||||||
spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value);
|
spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value);
|
||||||
@ -102,10 +128,11 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||||||
id = get_variant<std::string>(value);
|
id = get_variant<std::string>(value);
|
||||||
} else if (name == "Title") {
|
} else if (name == "Title") {
|
||||||
title = get_variant<std::string>(value);
|
title = get_variant<std::string>(value);
|
||||||
|
if (tooltip.text.empty()) {
|
||||||
|
event_box.set_tooltip_markup(title);
|
||||||
|
}
|
||||||
} else if (name == "Status") {
|
} else if (name == "Status") {
|
||||||
status = get_variant<std::string>(value);
|
setStatus(get_variant<Glib::ustring>(value));
|
||||||
} else if (name == "WindowId") {
|
|
||||||
window_id = get_variant<int32_t>(value);
|
|
||||||
} else if (name == "IconName") {
|
} else if (name == "IconName") {
|
||||||
icon_name = get_variant<std::string>(value);
|
icon_name = get_variant<std::string>(value);
|
||||||
} else if (name == "IconPixmap") {
|
} else if (name == "IconPixmap") {
|
||||||
@ -121,7 +148,10 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||||||
} else if (name == "AttentionMovieName") {
|
} else if (name == "AttentionMovieName") {
|
||||||
attention_movie_name = get_variant<std::string>(value);
|
attention_movie_name = get_variant<std::string>(value);
|
||||||
} else if (name == "ToolTip") {
|
} else if (name == "ToolTip") {
|
||||||
// TODO: tooltip
|
tooltip = get_variant<ToolTip>(value);
|
||||||
|
if (!tooltip.text.empty()) {
|
||||||
|
event_box.set_tooltip_markup(tooltip.text);
|
||||||
|
}
|
||||||
} else if (name == "IconThemePath") {
|
} else if (name == "IconThemePath") {
|
||||||
icon_theme_path = get_variant<std::string>(value);
|
icon_theme_path = get_variant<std::string>(value);
|
||||||
if (!icon_theme_path.empty()) {
|
if (!icon_theme_path.empty()) {
|
||||||
@ -148,9 +178,22 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Item::getUpdatedProperties() {
|
void Item::setStatus(const Glib::ustring& value) {
|
||||||
update_pending_ = false;
|
Glib::ustring lower = value.lowercase();
|
||||||
|
event_box.set_visible(show_passive_ || lower.compare("passive") != 0);
|
||||||
|
|
||||||
|
auto style = event_box.get_style_context();
|
||||||
|
for (const auto& class_name : style->list_classes()) {
|
||||||
|
style->remove_class(class_name);
|
||||||
|
}
|
||||||
|
if (lower.compare("needsattention") == 0) {
|
||||||
|
// convert status to dash-case for CSS
|
||||||
|
lower = "needs-attention";
|
||||||
|
}
|
||||||
|
style->add_class(lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Item::getUpdatedProperties() {
|
||||||
auto params = Glib::VariantContainerBase::create_tuple(
|
auto params = Glib::VariantContainerBase::create_tuple(
|
||||||
{Glib::Variant<Glib::ustring>::create(SNI_INTERFACE_NAME)});
|
{Glib::Variant<Glib::ustring>::create(SNI_INTERFACE_NAME)});
|
||||||
proxy_->call("org.freedesktop.DBus.Properties.GetAll",
|
proxy_->call("org.freedesktop.DBus.Properties.GetAll",
|
||||||
@ -167,33 +210,48 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
|
|||||||
auto properties = properties_variant.get();
|
auto properties = properties_variant.get();
|
||||||
|
|
||||||
for (const auto& [name, value] : properties) {
|
for (const auto& [name, value] : properties) {
|
||||||
Glib::VariantBase old_value;
|
if (update_pending_.count(name.raw())) {
|
||||||
proxy_->get_cached_property(old_value, name);
|
|
||||||
if (!old_value || !value.equal(old_value)) {
|
|
||||||
proxy_->set_cached_property(name, value);
|
|
||||||
setProperty(name, const_cast<Glib::VariantBase&>(value));
|
setProperty(name, const_cast<Glib::VariantBase&>(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->updateImage();
|
this->updateImage();
|
||||||
// this->event_box.set_tooltip_text(this->title);
|
|
||||||
} catch (const Glib::Error& err) {
|
} catch (const Glib::Error& err) {
|
||||||
spdlog::warn("Failed to update properties: {}", err.what());
|
spdlog::warn("Failed to update properties: {}", err.what());
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
spdlog::warn("Failed to update properties: {}", err.what());
|
spdlog::warn("Failed to update properties: {}", err.what());
|
||||||
}
|
}
|
||||||
|
update_pending_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping from a signal name to a set of possibly changed properties.
|
||||||
|
* Commented signals are not handled by the tray module at the moment.
|
||||||
|
*/
|
||||||
|
static const std::map<std::string_view, std::set<std::string_view>> signal2props = {
|
||||||
|
{"NewTitle", {"Title"}},
|
||||||
|
{"NewIcon", {"IconName", "IconPixmap"}},
|
||||||
|
// {"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}},
|
||||||
|
// {"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}},
|
||||||
|
{"NewIconThemePath", {"IconThemePath"}},
|
||||||
|
{"NewToolTip", {"ToolTip"}},
|
||||||
|
{"NewStatus", {"Status"}},
|
||||||
|
// {"XAyatanaNewLabel", {"XAyatanaLabel"}},
|
||||||
|
};
|
||||||
|
|
||||||
void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
||||||
const Glib::VariantContainerBase& arguments) {
|
const Glib::VariantContainerBase& arguments) {
|
||||||
spdlog::trace("Tray item '{}' got signal {}", id, signal_name);
|
spdlog::trace("Tray item '{}' got signal {}", id, signal_name);
|
||||||
if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) {
|
auto changed = signal2props.find(signal_name.raw());
|
||||||
/* Debounce signals and schedule update of all properties.
|
if (changed != signal2props.end()) {
|
||||||
* Based on behavior of Plasma dataengine for StatusNotifierItem.
|
if (update_pending_.empty()) {
|
||||||
*/
|
/* Debounce signals and schedule update of all properties.
|
||||||
update_pending_ = true;
|
* Based on behavior of Plasma dataengine for StatusNotifierItem.
|
||||||
Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties),
|
*/
|
||||||
UPDATE_DEBOUNCE_TIME);
|
Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties),
|
||||||
|
UPDATE_DEBOUNCE_TIME);
|
||||||
|
}
|
||||||
|
update_pending_.insert(changed->second.begin(), changed->second.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,35 +310,41 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Item::updateImage() {
|
void Item::updateImage() {
|
||||||
image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU);
|
auto pixbuf = getIconPixbuf();
|
||||||
image.set_pixel_size(icon_size);
|
auto scaled_icon_size = getScaledIconSize();
|
||||||
if (!icon_name.empty()) {
|
|
||||||
try {
|
if (!pixbuf) {
|
||||||
// Try to find icons specified by path and filename
|
pixbuf = getIconByName("image-missing", getScaledIconSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the loaded icon is not square, assume that the icon height should match the
|
||||||
|
// requested icon size, but the width is allowed to be different. As such, if the
|
||||||
|
// height of the image does not match the requested icon size, resize the icon such that
|
||||||
|
// the aspect ratio is maintained, but the height matches the requested icon size.
|
||||||
|
if (pixbuf->get_height() != scaled_icon_size) {
|
||||||
|
int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height();
|
||||||
|
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, 0, image.get_window());
|
||||||
|
image.set(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() {
|
||||||
|
try {
|
||||||
|
if (!icon_name.empty()) {
|
||||||
std::ifstream temp(icon_name);
|
std::ifstream temp(icon_name);
|
||||||
if (temp.is_open()) {
|
if (temp.is_open()) {
|
||||||
auto pixbuf = Gdk::Pixbuf::create_from_file(icon_name);
|
return Gdk::Pixbuf::create_from_file(icon_name);
|
||||||
if (pixbuf->gobj() != nullptr) {
|
|
||||||
// An icon specified by path and filename may be the wrong size for
|
|
||||||
// the tray
|
|
||||||
// Keep the aspect ratio and scale to make the height equal to icon_size
|
|
||||||
// If people have non square icons, assume they want it to grow in width not height
|
|
||||||
int width = icon_size * pixbuf->get_width() / pixbuf->get_height();
|
|
||||||
|
|
||||||
pixbuf = pixbuf->scale_simple(width, icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
|
||||||
image.set(pixbuf);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
image.set(getIconByName(icon_name, icon_size));
|
|
||||||
}
|
}
|
||||||
} catch (Glib::Error& e) {
|
return getIconByName(icon_name, getScaledIconSize());
|
||||||
spdlog::error("Item '{}': {}", id, static_cast<std::string>(e.what()));
|
} else if (icon_pixmap) {
|
||||||
|
return icon_pixmap;
|
||||||
}
|
}
|
||||||
} else if (icon_pixmap) {
|
} catch (Glib::Error& e) {
|
||||||
// An icon extracted may be the wrong size for the tray
|
spdlog::error("Item '{}': {}", id, static_cast<std::string>(e.what()));
|
||||||
icon_pixmap = icon_pixmap->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
}
|
||||||
image.set(icon_pixmap);
|
return getIconByName("image-missing", getScaledIconSize());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int request_size) {
|
Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int request_size) {
|
||||||
@ -315,6 +379,11 @@ Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int reque
|
|||||||
name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
|
name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Item::getScaledIconSize() {
|
||||||
|
// apply the scale factor from the Gtk window to the requested icon size
|
||||||
|
return icon_size * image.get_scale_factor();
|
||||||
|
}
|
||||||
|
|
||||||
void Item::onMenuDestroyed(Item* self, GObject* old_menu_pointer) {
|
void Item::onMenuDestroyed(Item* self, GObject* old_menu_pointer) {
|
||||||
if (old_menu_pointer == reinterpret_cast<GObject*>(self->dbus_menu)) {
|
if (old_menu_pointer == reinterpret_cast<GObject*>(self->dbus_menu)) {
|
||||||
self->gtk_menu = nullptr;
|
self->gtk_menu = nullptr;
|
||||||
@ -360,4 +429,52 @@ bool Item::handleClick(GdkEventButton* const& ev) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Item::handleScroll(GdkEventScroll* const& ev) {
|
||||||
|
int dx = 0, dy = 0;
|
||||||
|
switch (ev->direction) {
|
||||||
|
case GDK_SCROLL_UP:
|
||||||
|
dy = -1;
|
||||||
|
break;
|
||||||
|
case GDK_SCROLL_DOWN:
|
||||||
|
dy = 1;
|
||||||
|
break;
|
||||||
|
case GDK_SCROLL_LEFT:
|
||||||
|
dx = -1;
|
||||||
|
break;
|
||||||
|
case GDK_SCROLL_RIGHT:
|
||||||
|
dx = 1;
|
||||||
|
break;
|
||||||
|
case GDK_SCROLL_SMOOTH:
|
||||||
|
distance_scrolled_x_ += ev->delta_x;
|
||||||
|
distance_scrolled_y_ += ev->delta_y;
|
||||||
|
// check against the configured threshold and ensure that the absolute value >= 1
|
||||||
|
if (distance_scrolled_x_ > scroll_threshold_) {
|
||||||
|
dx = (int)lround(std::max(distance_scrolled_x_, 1.0));
|
||||||
|
distance_scrolled_x_ = 0;
|
||||||
|
} else if (distance_scrolled_x_ < -scroll_threshold_) {
|
||||||
|
dx = (int)lround(std::min(distance_scrolled_x_, -1.0));
|
||||||
|
distance_scrolled_x_ = 0;
|
||||||
|
}
|
||||||
|
if (distance_scrolled_y_ > scroll_threshold_) {
|
||||||
|
dy = (int)lround(std::max(distance_scrolled_y_, 1.0));
|
||||||
|
distance_scrolled_y_ = 0;
|
||||||
|
} else if (distance_scrolled_y_ < -scroll_threshold_) {
|
||||||
|
dy = (int)lround(std::min(distance_scrolled_y_, -1.0));
|
||||||
|
distance_scrolled_y_ = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (dx != 0) {
|
||||||
|
auto parameters = Glib::VariantContainerBase::create_tuple(
|
||||||
|
{Glib::Variant<int>::create(dx), Glib::Variant<Glib::ustring>::create("horizontal")});
|
||||||
|
proxy_->call("Scroll", parameters);
|
||||||
|
}
|
||||||
|
if (dy != 0) {
|
||||||
|
auto parameters = Glib::VariantContainerBase::create_tuple(
|
||||||
|
{Glib::Variant<int>::create(dy), Glib::Variant<Glib::ustring>::create("vertical")});
|
||||||
|
proxy_->call("Scroll", parameters);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules::SNI
|
} // namespace waybar::modules::SNI
|
||||||
|
@ -1,10 +1,28 @@
|
|||||||
#include "modules/sway/language.hpp"
|
#include "modules/sway/language.hpp"
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <json/json.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <xkbcommon/xkbregistry.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "modules/sway/ipc/ipc.hpp"
|
||||||
|
#include "util/string.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::sway {
|
namespace waybar::modules::sway {
|
||||||
|
|
||||||
|
const std::string Language::XKB_LAYOUT_NAMES_KEY = "xkb_layout_names";
|
||||||
|
const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name";
|
||||||
|
|
||||||
Language::Language(const std::string& id, const Json::Value& config)
|
Language::Language(const std::string& id, const Json::Value& config)
|
||||||
: ALabel(config, "language", id, "{}", 0, true) {
|
: ALabel(config, "language", id, "{}", 0, true) {
|
||||||
|
is_variant_displayed = format_.find("{variant}") != std::string::npos;
|
||||||
|
if (config.isMember("tooltip-format")) {
|
||||||
|
tooltip_format_ = config["tooltip-format"].asString();
|
||||||
|
}
|
||||||
ipc_.subscribe(R"(["input"])");
|
ipc_.subscribe(R"(["input"])");
|
||||||
ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent));
|
ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent));
|
||||||
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd));
|
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd));
|
||||||
@ -21,18 +39,31 @@ Language::Language(const std::string& id, const Json::Value& config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Language::onCmd(const struct Ipc::ipc_response& res) {
|
void Language::onCmd(const struct Ipc::ipc_response& res) {
|
||||||
|
if (res.type != static_cast<uint32_t>(IPC_GET_INPUTS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto payload = parser_.parse(res.payload);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
//Display current layout of a device with a maximum count of layouts, expecting that all will be OK
|
auto payload = parser_.parse(res.payload);
|
||||||
Json::Value::ArrayIndex maxId = 0, max = 0;
|
std::vector<std::string> used_layouts;
|
||||||
for(Json::Value::ArrayIndex i = 0; i < payload.size(); i++) {
|
// Display current layout of a device with a maximum count of layouts, expecting that all will
|
||||||
if(payload[i]["xkb_layout_names"].size() > max) {
|
// be OK
|
||||||
max = payload[i]["xkb_layout_names"].size();
|
Json::ArrayIndex max_id = 0, max = 0;
|
||||||
maxId = i;
|
for (Json::ArrayIndex i = 0; i < payload.size(); i++) {
|
||||||
|
auto size = payload[i][XKB_LAYOUT_NAMES_KEY].size();
|
||||||
|
if (size > max) {
|
||||||
|
max = size;
|
||||||
|
max_id = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto layout_name = payload[maxId]["xkb_active_layout_name"].asString().substr(0,2);
|
|
||||||
lang_ = Glib::Markup::escape_text(layout_name);
|
for (const auto& layout : payload[max_id][XKB_LAYOUT_NAMES_KEY]) {
|
||||||
|
used_layouts.push_back(layout.asString());
|
||||||
|
}
|
||||||
|
|
||||||
|
init_layouts_map(used_layouts);
|
||||||
|
set_current_layout(payload[max_id][XKB_ACTIVE_LAYOUT_NAME_KEY].asString());
|
||||||
dp.emit();
|
dp.emit();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
spdlog::error("Language: {}", e.what());
|
spdlog::error("Language: {}", e.what());
|
||||||
@ -40,12 +71,15 @@ void Language::onCmd(const struct Ipc::ipc_response& res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Language::onEvent(const struct Ipc::ipc_response& res) {
|
void Language::onEvent(const struct Ipc::ipc_response& res) {
|
||||||
|
if (res.type != static_cast<uint32_t>(IPC_EVENT_INPUT)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
auto payload = parser_.parse(res.payload)["input"];
|
auto payload = parser_.parse(res.payload)["input"];
|
||||||
if (payload["type"].asString() == "keyboard") {
|
if (payload["type"].asString() == "keyboard") {
|
||||||
auto layout_name = payload["xkb_active_layout_name"].asString().substr(0,2);
|
set_current_layout(payload[XKB_ACTIVE_LAYOUT_NAME_KEY].asString());
|
||||||
lang_ = Glib::Markup::escape_text(layout_name);
|
|
||||||
}
|
}
|
||||||
dp.emit();
|
dp.emit();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
@ -54,17 +88,102 @@ void Language::onEvent(const struct Ipc::ipc_response& res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Language::update() -> void {
|
auto Language::update() -> void {
|
||||||
if (lang_.empty()) {
|
auto display_layout = trim(fmt::format(format_,
|
||||||
event_box_.hide();
|
fmt::arg("short", layout_.short_name),
|
||||||
} else {
|
fmt::arg("long", layout_.full_name),
|
||||||
label_.set_markup(fmt::format(format_, lang_));
|
fmt::arg("variant", layout_.variant)));
|
||||||
if (tooltipEnabled()) {
|
label_.set_markup(display_layout);
|
||||||
label_.set_tooltip_text(lang_);
|
if (tooltipEnabled()) {
|
||||||
}
|
if (tooltip_format_ != "") {
|
||||||
event_box_.show();
|
auto tooltip_display_layout = trim(fmt::format(tooltip_format_,
|
||||||
|
fmt::arg("short", layout_.short_name),
|
||||||
|
fmt::arg("long", layout_.full_name),
|
||||||
|
fmt::arg("variant", layout_.variant)));
|
||||||
|
label_.set_tooltip_markup(tooltip_display_layout);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
label_.set_tooltip_markup(display_layout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event_box_.show();
|
||||||
|
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Language::set_current_layout(std::string current_layout) -> void {
|
||||||
|
layout_ = layouts_map_[current_layout];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Language::init_layouts_map(const std::vector<std::string>& used_layouts) -> void {
|
||||||
|
std::map<std::string, std::vector<Layout*>> found_by_short_names;
|
||||||
|
auto layout = xkb_context_.next_layout();
|
||||||
|
for (; layout != nullptr; layout = xkb_context_.next_layout()) {
|
||||||
|
if (std::find(used_layouts.begin(), used_layouts.end(), layout->full_name) ==
|
||||||
|
used_layouts.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_variant_displayed) {
|
||||||
|
auto short_name = layout->short_name;
|
||||||
|
if (found_by_short_names.count(short_name) > 0) {
|
||||||
|
found_by_short_names[short_name].push_back(layout);
|
||||||
|
} else {
|
||||||
|
found_by_short_names[short_name] = {layout};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layouts_map_.emplace(layout->full_name, *layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_variant_displayed || found_by_short_names.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, int> short_name_to_number_map;
|
||||||
|
for (const auto& used_layout_name : used_layouts) {
|
||||||
|
auto used_layout = &layouts_map_.find(used_layout_name)->second;
|
||||||
|
auto layouts_with_same_name_list = found_by_short_names[used_layout->short_name];
|
||||||
|
spdlog::info("SIZE: " + std::to_string(layouts_with_same_name_list.size()));
|
||||||
|
if (layouts_with_same_name_list.size() < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (short_name_to_number_map.count(used_layout->short_name) == 0) {
|
||||||
|
short_name_to_number_map[used_layout->short_name] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
used_layout->short_name =
|
||||||
|
used_layout->short_name + std::to_string(short_name_to_number_map[used_layout->short_name]++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Language::XKBContext::XKBContext() {
|
||||||
|
context_ = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
|
||||||
|
rxkb_context_include_path_append_default(context_);
|
||||||
|
rxkb_context_parse_default_ruleset(context_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Language::XKBContext::next_layout() -> Layout* {
|
||||||
|
if (xkb_layout_ == nullptr) {
|
||||||
|
xkb_layout_ = rxkb_layout_first(context_);
|
||||||
|
} else {
|
||||||
|
xkb_layout_ = rxkb_layout_next(xkb_layout_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xkb_layout_ == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto description = std::string(rxkb_layout_get_description(xkb_layout_));
|
||||||
|
auto name = std::string(rxkb_layout_get_name(xkb_layout_));
|
||||||
|
auto variant_ = rxkb_layout_get_variant(xkb_layout_);
|
||||||
|
std::string variant = variant_ == nullptr ? "" : std::string(variant_);
|
||||||
|
|
||||||
|
layout_ = new Layout{description, name, variant};
|
||||||
|
return layout_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Language::XKBContext::~XKBContext() { rxkb_context_unref(context_); }
|
||||||
} // namespace waybar::modules::sway
|
} // namespace waybar::modules::sway
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "modules/sway/window.hpp"
|
#include "modules/sway/window.hpp"
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
namespace waybar::modules::sway {
|
namespace waybar::modules::sway {
|
||||||
|
|
||||||
@ -56,7 +57,8 @@ auto Window::update() -> void {
|
|||||||
bar_.window.get_style_context()->remove_class("solo");
|
bar_.window.get_style_context()->remove_class("solo");
|
||||||
bar_.window.get_style_context()->remove_class("empty");
|
bar_.window.get_style_context()->remove_class("empty");
|
||||||
}
|
}
|
||||||
label_.set_markup(fmt::format(format_, window_));
|
label_.set_markup(fmt::format(format_, fmt::arg("title", rewriteTitle(window_)),
|
||||||
|
fmt::arg("app_id", app_id_)));
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
label_.set_tooltip_text(window_);
|
label_.set_tooltip_text(window_);
|
||||||
}
|
}
|
||||||
@ -64,29 +66,51 @@ auto Window::update() -> void {
|
|||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<std::size_t, int, std::string, std::string> Window::getFocusedNode(
|
int leafNodesInWorkspace(const Json::Value& node) {
|
||||||
const Json::Value& nodes, std::string& output) {
|
auto const& nodes = node["nodes"];
|
||||||
for (auto const& node : nodes) {
|
if(nodes.empty()) {
|
||||||
|
if(node["type"] == "workspace")
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int sum = 0;
|
||||||
|
for(auto const& node : nodes)
|
||||||
|
sum += leafNodesInWorkspace(node);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<std::size_t, int, std::string, std::string> gfnWithWorkspace(
|
||||||
|
const Json::Value& nodes, std::string& output, const Json::Value& config_,
|
||||||
|
const Bar& bar_, Json::Value& parentWorkspace) {
|
||||||
|
for(auto const& node : nodes) {
|
||||||
if (node["output"].isString()) {
|
if (node["output"].isString()) {
|
||||||
output = node["output"].asString();
|
output = node["output"].asString();
|
||||||
}
|
}
|
||||||
|
// found node
|
||||||
if (node["focused"].asBool() && (node["type"] == "con" || node["type"] == "floating_con")) {
|
if (node["focused"].asBool() && (node["type"] == "con" || node["type"] == "floating_con")) {
|
||||||
if ((!config_["all-outputs"].asBool() && output == bar_.output->name) ||
|
if ((!config_["all-outputs"].asBool() && output == bar_.output->name) ||
|
||||||
config_["all-outputs"].asBool()) {
|
config_["all-outputs"].asBool()) {
|
||||||
auto app_id = node["app_id"].isString() ? node["app_id"].asString()
|
auto app_id = node["app_id"].isString() ? node["app_id"].asString()
|
||||||
: node["window_properties"]["instance"].asString();
|
: node["window_properties"]["instance"].asString();
|
||||||
return {nodes.size(),
|
int nb = node.size();
|
||||||
node["id"].asInt(),
|
if(parentWorkspace != 0)
|
||||||
Glib::Markup::escape_text(node["name"].asString()),
|
nb = leafNodesInWorkspace(parentWorkspace);
|
||||||
app_id};
|
return {nb,
|
||||||
|
node["id"].asInt(),
|
||||||
|
Glib::Markup::escape_text(node["name"].asString()),
|
||||||
|
app_id};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto [nb, id, name, app_id] = getFocusedNode(node["nodes"], output);
|
// iterate
|
||||||
|
if(node["type"] == "workspace")
|
||||||
|
parentWorkspace = node;
|
||||||
|
auto [nb, id, name, app_id] = gfnWithWorkspace(node["nodes"], output, config_, bar_, parentWorkspace);
|
||||||
if (id > -1 && !name.empty()) {
|
if (id > -1 && !name.empty()) {
|
||||||
return {nb, id, name, app_id};
|
return {nb, id, name, app_id};
|
||||||
}
|
}
|
||||||
// Search for floating node
|
// Search for floating node
|
||||||
std::tie(nb, id, name, app_id) = getFocusedNode(node["floating_nodes"], output);
|
std::tie(nb, id, name, app_id) = gfnWithWorkspace(node["floating_nodes"], output, config_, bar_, parentWorkspace);
|
||||||
if (id > -1 && !name.empty()) {
|
if (id > -1 && !name.empty()) {
|
||||||
return {nb, id, name, app_id};
|
return {nb, id, name, app_id};
|
||||||
}
|
}
|
||||||
@ -94,6 +118,12 @@ std::tuple<std::size_t, int, std::string, std::string> Window::getFocusedNode(
|
|||||||
return {0, -1, "", ""};
|
return {0, -1, "", ""};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::tuple<std::size_t, int, std::string, std::string> Window::getFocusedNode(
|
||||||
|
const Json::Value& nodes, std::string& output) {
|
||||||
|
Json::Value placeholder = 0;
|
||||||
|
return gfnWithWorkspace(nodes, output, config_, bar_, placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
void Window::getTree() {
|
void Window::getTree() {
|
||||||
try {
|
try {
|
||||||
ipc_.sendCmd(IPC_GET_TREE);
|
ipc_.sendCmd(IPC_GET_TREE);
|
||||||
@ -102,4 +132,30 @@ void Window::getTree() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Window::rewriteTitle(const std::string& title) {
|
||||||
|
const auto& rules = config_["rewrite"];
|
||||||
|
if (!rules.isObject()) {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string res = title;
|
||||||
|
|
||||||
|
for (auto it = rules.begin(); it != rules.end(); ++it) {
|
||||||
|
if (it.key().isString() && it->isString()) {
|
||||||
|
try {
|
||||||
|
// malformated regexes will cause an exception.
|
||||||
|
// in this case, log error and try the next rule.
|
||||||
|
const std::regex rule{it.key().asString()};
|
||||||
|
if (std::regex_match(title, rule)) {
|
||||||
|
res = std::regex_replace(res, rule, it->asString());
|
||||||
|
}
|
||||||
|
} catch (const std::regex_error& e) {
|
||||||
|
spdlog::error("Invalid rule {}: {}", it.key().asString(), e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules::sway
|
} // namespace waybar::modules::sway
|
||||||
|
@ -257,11 +257,19 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) {
|
|||||||
ipc_.sendCmd(
|
ipc_.sendCmd(
|
||||||
IPC_COMMAND,
|
IPC_COMMAND,
|
||||||
fmt::format(workspace_switch_cmd_ + "; move workspace to output \"{}\"; " + workspace_switch_cmd_,
|
fmt::format(workspace_switch_cmd_ + "; move workspace to output \"{}\"; " + workspace_switch_cmd_,
|
||||||
|
"--no-auto-back-and-forth",
|
||||||
node["name"].asString(),
|
node["name"].asString(),
|
||||||
node["target_output"].asString(),
|
node["target_output"].asString(),
|
||||||
|
"--no-auto-back-and-forth",
|
||||||
node["name"].asString()));
|
node["name"].asString()));
|
||||||
} else {
|
} else {
|
||||||
ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, node["name"].asString()));
|
ipc_.sendCmd(
|
||||||
|
IPC_COMMAND,
|
||||||
|
fmt::format("workspace {} \"{}\"",
|
||||||
|
config_["disable-auto-back-and-forth"].asBool()
|
||||||
|
? "--no-auto-back-and-forth"
|
||||||
|
: "",
|
||||||
|
node["name"].asString()));
|
||||||
}
|
}
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
spdlog::error("Workspaces: {}", e.what());
|
spdlog::error("Workspaces: {}", e.what());
|
||||||
@ -322,7 +330,9 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, name));
|
ipc_.sendCmd(
|
||||||
|
IPC_COMMAND,
|
||||||
|
fmt::format(workspace_switch_cmd_, "--no-auto-back-and-forth", name));
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
spdlog::error("Workspaces: {}", e.what());
|
spdlog::error("Workspaces: {}", e.what());
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,16 @@ auto waybar::modules::Temperature::update() -> void {
|
|||||||
fmt::arg("temperatureF", temperature_f),
|
fmt::arg("temperatureF", temperature_f),
|
||||||
fmt::arg("temperatureK", temperature_k),
|
fmt::arg("temperatureK", temperature_k),
|
||||||
fmt::arg("icon", getIcon(temperature_c, "", max_temp))));
|
fmt::arg("icon", getIcon(temperature_c, "", max_temp))));
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
std::string tooltip_format = "{temperatureC}°C";
|
||||||
|
if (config_["tooltip-format"].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
}
|
||||||
|
label_.set_tooltip_text(fmt::format(tooltip_format,
|
||||||
|
fmt::arg("temperatureC", temperature_c),
|
||||||
|
fmt::arg("temperatureF", temperature_f),
|
||||||
|
fmt::arg("temperatureK", temperature_k)));
|
||||||
|
}
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "modules/wlr/taskbar.hpp"
|
#include "modules/wlr/taskbar.hpp"
|
||||||
|
|
||||||
|
#include "glibmm/error.h"
|
||||||
|
#include "glibmm/fileutils.h"
|
||||||
#include "glibmm/refptr.h"
|
#include "glibmm/refptr.h"
|
||||||
#include "util/format.hpp"
|
#include "util/format.hpp"
|
||||||
|
|
||||||
@ -15,6 +17,7 @@
|
|||||||
#include <gtkmm/icontheme.h>
|
#include <gtkmm/icontheme.h>
|
||||||
|
|
||||||
#include <giomm/desktopappinfo.h>
|
#include <giomm/desktopappinfo.h>
|
||||||
|
#include <gio/gdesktopappinfo.h>
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
@ -26,19 +29,19 @@ const std::string WHITESPACE = " \n\r\t\f\v";
|
|||||||
|
|
||||||
static std::string ltrim(const std::string& s)
|
static std::string ltrim(const std::string& s)
|
||||||
{
|
{
|
||||||
size_t start = s.find_first_not_of(WHITESPACE);
|
size_t start = s.find_first_not_of(WHITESPACE);
|
||||||
return (start == std::string::npos) ? "" : s.substr(start);
|
return (start == std::string::npos) ? "" : s.substr(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string rtrim(const std::string& s)
|
static std::string rtrim(const std::string& s)
|
||||||
{
|
{
|
||||||
size_t end = s.find_last_not_of(WHITESPACE);
|
size_t end = s.find_last_not_of(WHITESPACE);
|
||||||
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string trim(const std::string& s)
|
static std::string trim(const std::string& s)
|
||||||
{
|
{
|
||||||
return rtrim(ltrim(s));
|
return rtrim(ltrim(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -64,12 +67,25 @@ static std::vector<std::string> search_prefix()
|
|||||||
} while(end != std::string::npos);
|
} while(end != std::string::npos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string home_dir = std::getenv("HOME");
|
||||||
|
prefixes.push_back(home_dir + "/.local/share/");
|
||||||
|
|
||||||
for (auto& p : prefixes)
|
for (auto& p : prefixes)
|
||||||
spdlog::debug("Using 'desktop' search path prefix: {}", p);
|
spdlog::debug("Using 'desktop' search path prefix: {}", p);
|
||||||
|
|
||||||
return prefixes;
|
return prefixes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Glib::RefPtr<Gdk::Pixbuf> load_icon_from_file(std::string icon_path, int size)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
auto pb = Gdk::Pixbuf::create_from_file(icon_path, size, size);
|
||||||
|
return pb;
|
||||||
|
} catch(...) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Method 1 - get the correct icon name from the desktop file */
|
/* Method 1 - get the correct icon name from the desktop file */
|
||||||
static std::string get_from_desktop_app_info(const std::string &app_id)
|
static std::string get_from_desktop_app_info(const std::string &app_id)
|
||||||
{
|
{
|
||||||
@ -103,14 +119,41 @@ static std::string get_from_desktop_app_info(const std::string &app_id)
|
|||||||
|
|
||||||
/* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */
|
/* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */
|
||||||
static std::string get_from_icon_theme(const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
|
static std::string get_from_icon_theme(const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
|
||||||
const std::string &app_id) {
|
const std::string &app_id)
|
||||||
|
{
|
||||||
if (icon_theme->lookup_icon(app_id, 24))
|
if (icon_theme->lookup_icon(app_id, 24))
|
||||||
return app_id;
|
return app_id;
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Method 3 - as last resort perform a search for most appropriate desktop info file */
|
||||||
|
static std::string get_from_desktop_app_info_search(const std::string &app_id)
|
||||||
|
{
|
||||||
|
std::string desktop_file = "";
|
||||||
|
|
||||||
|
gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str());
|
||||||
|
if (desktop_list != nullptr && desktop_list[0] != nullptr) {
|
||||||
|
for (size_t i=0; desktop_list[0][i]; i++) {
|
||||||
|
if (desktop_file == "") {
|
||||||
|
desktop_file = desktop_list[0][i];
|
||||||
|
} else {
|
||||||
|
auto tmp_info = Gio::DesktopAppInfo::create(desktop_list[0][i]);
|
||||||
|
auto startup_class = tmp_info->get_startup_wm_class();
|
||||||
|
|
||||||
|
if (startup_class == app_id) {
|
||||||
|
desktop_file = desktop_list[0][i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_strfreev(desktop_list[0]);
|
||||||
|
}
|
||||||
|
g_free(desktop_list);
|
||||||
|
|
||||||
|
return get_from_desktop_app_info(desktop_file);
|
||||||
|
}
|
||||||
|
|
||||||
static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
|
static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
|
||||||
const std::string &app_id_list, int size)
|
const std::string &app_id_list, int size)
|
||||||
{
|
{
|
||||||
@ -122,6 +165,10 @@ static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme
|
|||||||
* send a single app-id, but in any case this works fine */
|
* send a single app-id, but in any case this works fine */
|
||||||
while (stream >> app_id)
|
while (stream >> app_id)
|
||||||
{
|
{
|
||||||
|
size_t start = 0, end = app_id.size();
|
||||||
|
start = app_id.rfind(".", end);
|
||||||
|
std::string app_name = app_id.substr(start+1, app_id.size());
|
||||||
|
|
||||||
auto lower_app_id = app_id;
|
auto lower_app_id = app_id;
|
||||||
std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(),
|
std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(),
|
||||||
[](char c){ return std::tolower(c); });
|
[](char c){ return std::tolower(c); });
|
||||||
@ -130,15 +177,31 @@ static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme
|
|||||||
|
|
||||||
if (icon_name.empty())
|
if (icon_name.empty())
|
||||||
icon_name = get_from_icon_theme(icon_theme, lower_app_id);
|
icon_name = get_from_icon_theme(icon_theme, lower_app_id);
|
||||||
|
if (icon_name.empty())
|
||||||
|
icon_name = get_from_icon_theme(icon_theme, app_name);
|
||||||
if (icon_name.empty())
|
if (icon_name.empty())
|
||||||
icon_name = get_from_desktop_app_info(app_id);
|
icon_name = get_from_desktop_app_info(app_id);
|
||||||
if (icon_name.empty())
|
if (icon_name.empty())
|
||||||
icon_name = get_from_desktop_app_info(lower_app_id);
|
icon_name = get_from_desktop_app_info(lower_app_id);
|
||||||
|
if (icon_name.empty())
|
||||||
|
icon_name = get_from_desktop_app_info(app_name);
|
||||||
|
if (icon_name.empty())
|
||||||
|
icon_name = get_from_desktop_app_info_search(app_id);
|
||||||
|
|
||||||
if (icon_name.empty())
|
if (icon_name.empty())
|
||||||
continue;
|
icon_name = "unknown";
|
||||||
|
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||||
|
|
||||||
|
try {
|
||||||
|
pixbuf = icon_theme->load_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE);
|
||||||
|
} catch(...) {
|
||||||
|
if (Glib::file_test(icon_name, Glib::FILE_TEST_EXISTS))
|
||||||
|
pixbuf = load_icon_from_file(icon_name, size);
|
||||||
|
else
|
||||||
|
pixbuf = {};
|
||||||
|
}
|
||||||
|
|
||||||
auto pixbuf = icon_theme->load_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE);
|
|
||||||
if (pixbuf) {
|
if (pixbuf) {
|
||||||
image.set(pixbuf);
|
image.set(pixbuf);
|
||||||
found = true;
|
found = true;
|
||||||
@ -214,7 +277,7 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar,
|
|||||||
bar_{bar}, config_{config}, tbar_{tbar}, handle_{tl_handle}, seat_{seat},
|
bar_{bar}, config_{config}, tbar_{tbar}, handle_{tl_handle}, seat_{seat},
|
||||||
id_{global_id++},
|
id_{global_id++},
|
||||||
content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
||||||
button_visible_{false}
|
button_visible_{false}, ignored_{false}
|
||||||
{
|
{
|
||||||
zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this);
|
zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this);
|
||||||
|
|
||||||
@ -320,6 +383,21 @@ void Task::handle_app_id(const char *app_id)
|
|||||||
{
|
{
|
||||||
app_id_ = app_id;
|
app_id_ = app_id;
|
||||||
|
|
||||||
|
if (tbar_->ignore_list().count(app_id)) {
|
||||||
|
ignored_ = true;
|
||||||
|
if (button_visible_) {
|
||||||
|
auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
||||||
|
handle_output_leave(output);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool is_was_ignored = ignored_;
|
||||||
|
ignored_ = false;
|
||||||
|
if (is_was_ignored) {
|
||||||
|
auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
||||||
|
handle_output_enter(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!with_icon_)
|
if (!with_icon_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -342,6 +420,11 @@ void Task::handle_output_enter(struct wl_output *output)
|
|||||||
{
|
{
|
||||||
spdlog::debug("{} entered output {}", repr(), (void*)output);
|
spdlog::debug("{} entered output {}", repr(), (void*)output);
|
||||||
|
|
||||||
|
if (ignored_) {
|
||||||
|
spdlog::debug("{} is ignored", repr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) {
|
if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) {
|
||||||
/* The task entered the output of the current bar make the button visible */
|
/* The task entered the output of the current bar make the button visible */
|
||||||
tbar_->add_button(button_);
|
tbar_->add_button(button_);
|
||||||
@ -367,16 +450,16 @@ void Task::handle_output_leave(struct wl_output *output)
|
|||||||
void Task::handle_state(struct wl_array *state)
|
void Task::handle_state(struct wl_array *state)
|
||||||
{
|
{
|
||||||
state_ = 0;
|
state_ = 0;
|
||||||
for (auto* entry = static_cast<uint32_t*>(state->data);
|
size_t size = state->size / sizeof(uint32_t);
|
||||||
entry < static_cast<uint32_t*>(state->data) + state->size;
|
for (size_t i = 0; i < size; ++i) {
|
||||||
entry++) {
|
auto entry = static_cast<uint32_t*>(state->data)[i];
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED)
|
if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED)
|
||||||
state_ |= MAXIMIZED;
|
state_ |= MAXIMIZED;
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED)
|
if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED)
|
||||||
state_ |= MINIMIZED;
|
state_ |= MINIMIZED;
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)
|
if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)
|
||||||
state_ |= ACTIVE;
|
state_ |= ACTIVE;
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)
|
if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)
|
||||||
state_ |= FULLSCREEN;
|
state_ |= FULLSCREEN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -489,7 +572,7 @@ void Task::update()
|
|||||||
fmt::arg("state", state_string()),
|
fmt::arg("state", state_string()),
|
||||||
fmt::arg("short_state", state_string(true))
|
fmt::arg("short_state", state_string(true))
|
||||||
);
|
);
|
||||||
if (markup)
|
if (markup)
|
||||||
text_before_.set_markup(txt);
|
text_before_.set_markup(txt);
|
||||||
else
|
else
|
||||||
text_before_.set_label(txt);
|
text_before_.set_label(txt);
|
||||||
@ -502,7 +585,7 @@ void Task::update()
|
|||||||
fmt::arg("state", state_string()),
|
fmt::arg("state", state_string()),
|
||||||
fmt::arg("short_state", state_string(true))
|
fmt::arg("short_state", state_string(true))
|
||||||
);
|
);
|
||||||
if (markup)
|
if (markup)
|
||||||
text_after_.set_markup(txt);
|
text_after_.set_markup(txt);
|
||||||
else
|
else
|
||||||
text_after_.set_label(txt);
|
text_after_.set_label(txt);
|
||||||
@ -516,7 +599,7 @@ void Task::update()
|
|||||||
fmt::arg("state", state_string()),
|
fmt::arg("state", state_string()),
|
||||||
fmt::arg("short_state", state_string(true))
|
fmt::arg("short_state", state_string(true))
|
||||||
);
|
);
|
||||||
if (markup)
|
if (markup)
|
||||||
button_.set_tooltip_markup(txt);
|
button_.set_tooltip_markup(txt);
|
||||||
else
|
else
|
||||||
button_.set_tooltip_text(txt);
|
button_.set_tooltip_text(txt);
|
||||||
@ -584,7 +667,7 @@ static const wl_registry_listener registry_listener_impl = {
|
|||||||
.global_remove = handle_global_remove
|
.global_remove = handle_global_remove
|
||||||
};
|
};
|
||||||
|
|
||||||
Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Value &config)
|
Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Value &config)
|
||||||
: waybar::AModule(config, "taskbar", id, false, false),
|
: waybar::AModule(config, "taskbar", id, false, false),
|
||||||
bar_(bar),
|
bar_(bar),
|
||||||
box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
||||||
@ -631,14 +714,34 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu
|
|||||||
|
|
||||||
icon_themes_.push_back(it);
|
icon_themes_.push_back(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load ignore-list
|
||||||
|
if (config_["ignore-list"].isArray()) {
|
||||||
|
for (auto& app_name : config_["ignore-list"]) {
|
||||||
|
ignore_list_.emplace(app_name.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
icon_themes_.push_back(Gtk::IconTheme::get_default());
|
icon_themes_.push_back(Gtk::IconTheme::get_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
Taskbar::~Taskbar()
|
Taskbar::~Taskbar()
|
||||||
{
|
{
|
||||||
if (manager_) {
|
if (manager_) {
|
||||||
zwlr_foreign_toplevel_manager_v1_destroy(manager_);
|
struct wl_display *display = Client::inst()->wl_display;
|
||||||
manager_ = nullptr;
|
/*
|
||||||
|
* Send `stop` request and wait for one roundtrip.
|
||||||
|
* This is not quite correct as the protocol encourages us to wait for the .finished event,
|
||||||
|
* but it should work with wlroots foreign toplevel manager implementation.
|
||||||
|
*/
|
||||||
|
zwlr_foreign_toplevel_manager_v1_stop(manager_);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
if (manager_) {
|
||||||
|
spdlog::warn("Foreign toplevel manager destroyed before .finished event");
|
||||||
|
zwlr_foreign_toplevel_manager_v1_destroy(manager_);
|
||||||
|
manager_ = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -754,5 +857,6 @@ std::vector<Glib::RefPtr<Gtk::IconTheme>> Taskbar::icon_themes() const
|
|||||||
{
|
{
|
||||||
return icon_themes_;
|
return icon_themes_;
|
||||||
}
|
}
|
||||||
|
const std::unordered_set<std::string> &Taskbar::ignore_list() const { return ignore_list_; }
|
||||||
|
|
||||||
} /* namespace waybar::modules::wlr */
|
} /* namespace waybar::modules::wlr */
|
||||||
|
@ -19,60 +19,64 @@
|
|||||||
#include "util/rfkill.hpp"
|
#include "util/rfkill.hpp"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <glibmm/main.h>
|
||||||
#include <linux/rfkill.h>
|
#include <linux/rfkill.h>
|
||||||
#include <poll.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstring>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
waybar::util::Rfkill::Rfkill(const enum rfkill_type rfkill_type) : rfkill_type_(rfkill_type) {}
|
waybar::util::Rfkill::Rfkill(const enum rfkill_type rfkill_type) : rfkill_type_(rfkill_type) {
|
||||||
|
fd_ = open("/dev/rfkill", O_RDONLY);
|
||||||
void waybar::util::Rfkill::waitForEvent() {
|
if (fd_ < 0) {
|
||||||
struct rfkill_event event;
|
spdlog::error("Can't open RFKILL control device");
|
||||||
struct pollfd p;
|
|
||||||
ssize_t len;
|
|
||||||
int fd, n;
|
|
||||||
|
|
||||||
fd = open("/dev/rfkill", O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
throw std::runtime_error("Can't open RFKILL control device");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int rc = fcntl(fd_, F_SETFL, O_NONBLOCK);
|
||||||
memset(&p, 0, sizeof(p));
|
if (rc < 0) {
|
||||||
p.fd = fd;
|
spdlog::error("Can't set RFKILL control device to non-blocking: {}", errno);
|
||||||
p.events = POLLIN | POLLHUP;
|
close(fd_);
|
||||||
|
fd_ = -1;
|
||||||
while (1) {
|
return;
|
||||||
n = poll(&p, 1, -1);
|
|
||||||
if (n < 0) {
|
|
||||||
throw std::runtime_error("Failed to poll RFKILL control device");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == 0) continue;
|
|
||||||
|
|
||||||
len = read(fd, &event, sizeof(event));
|
|
||||||
if (len < 0) {
|
|
||||||
throw std::runtime_error("Reading of RFKILL events failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len != RFKILL_EVENT_SIZE_V1) {
|
|
||||||
throw std::runtime_error("Wrong size of RFKILL event");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.type == rfkill_type_ && event.op == RFKILL_OP_CHANGE) {
|
|
||||||
state_ = event.soft || event.hard;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Glib::signal_io().connect(
|
||||||
|
sigc::mem_fun(*this, &Rfkill::on_event), fd_, Glib::IO_IN | Glib::IO_ERR | Glib::IO_HUP);
|
||||||
|
}
|
||||||
|
|
||||||
close(fd);
|
waybar::util::Rfkill::~Rfkill() {
|
||||||
|
if (fd_ >= 0) {
|
||||||
|
close(fd_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool waybar::util::Rfkill::on_event(Glib::IOCondition cond) {
|
||||||
|
if (cond & Glib::IO_IN) {
|
||||||
|
struct rfkill_event event;
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
len = read(fd_, &event, sizeof(event));
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
spdlog::error("Reading of RFKILL events failed: {}", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len < RFKILL_EVENT_SIZE_V1) {
|
||||||
|
spdlog::error("Wrong size of RFKILL event: {} < {}", len, RFKILL_EVENT_SIZE_V1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == rfkill_type_ && (event.op == RFKILL_OP_ADD || event.op == RFKILL_OP_CHANGE)) {
|
||||||
|
state_ = event.soft || event.hard;
|
||||||
|
on_update.emit(event);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
spdlog::error("Failed to poll RFKILL control device");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::util::Rfkill::getState() const { return state_; }
|
bool waybar::util::Rfkill::getState() const { return state_; }
|
||||||
|
9
src/util/ustring_clen.cpp
Normal file
9
src/util/ustring_clen.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "util/ustring_clen.hpp"
|
||||||
|
|
||||||
|
int ustring_clen(const Glib::ustring &str){
|
||||||
|
int total = 0;
|
||||||
|
for (auto i = str.begin(); i != str.end(); ++i) {
|
||||||
|
total += g_unichar_iswide(*i) + 1;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
@ -1,13 +1,11 @@
|
|||||||
[wrap-file]
|
[wrap-file]
|
||||||
directory = spdlog-1.8.1
|
directory = spdlog-1.8.5
|
||||||
|
source_url = https://github.com/gabime/spdlog/archive/v1.8.5.tar.gz
|
||||||
source_url = https://github.com/gabime/spdlog/archive/v1.8.1.tar.gz
|
source_filename = v1.8.5.tar.gz
|
||||||
source_filename = v1.8.1.tar.gz
|
source_hash = 944d0bd7c763ac721398dca2bb0f3b5ed16f67cef36810ede5061f35a543b4b8
|
||||||
source_hash = 5197b3147cfcfaa67dd564db7b878e4a4b3d9f3443801722b3915cdeced656cb
|
patch_url = https://wrapdb.mesonbuild.com/v1/projects/spdlog/1.8.5/1/get_zip
|
||||||
|
patch_filename = spdlog-1.8.5-1-wrap.zip
|
||||||
patch_url = https://github.com/mesonbuild/spdlog/releases/download/1.8.1-1/spdlog.zip
|
patch_hash = 3c38f275d5792b1286391102594329e98b17737924b344f98312ab09929b74be
|
||||||
patch_filename = spdlog-1.8.1-1-wrap.zip
|
|
||||||
patch_hash = 76844292a8e912aec78450618271a311841b33b17000988f215ddd6c64dd71b3
|
|
||||||
|
|
||||||
[provide]
|
[provide]
|
||||||
spdlog = spdlog_dep
|
spdlog = spdlog_dep
|
||||||
|
Reference in New Issue
Block a user