├── .envrc ├── resources ├── custom_modules │ └── cffi_example │ │ ├── .gitignore │ │ ├── meson.build │ │ ├── README.md │ │ └── main.c ├── icons │ ├── meson.build │ ├── waybar_icons.gresource.xml │ ├── waybar-privacy-audio-input-symbolic.svg │ └── waybar-privacy-screen-share-symbolic.svg └── waybar.service.in ├── test ├── config │ ├── include-2.json │ ├── include-multi-1.json │ ├── include-multi-3-0.json │ ├── include-multi-0.json │ ├── include-multi-2.json │ ├── simple.json │ ├── include-multi-3.json │ ├── include.json │ ├── include-1.json │ ├── include-multi.json │ └── multi.json ├── GlibTestsFixture.hpp ├── meson.build ├── main.cpp └── JsonParser.cpp ├── preview-2.png ├── preview.png ├── .clang-format ├── .gitmodules ├── .github ├── FUNDING.yml ├── workflows │ ├── labeler.yml │ ├── clang-format.yml │ ├── linux.yml │ ├── freebsd.yml │ └── clang-tidy.yml └── labeler.yml ├── include ├── util │ ├── ustring_clen.hpp │ ├── sanitize_str.hpp │ ├── backend_common.hpp │ ├── prepare_for_sleep.h │ ├── rewrite_string.hpp │ ├── gtk_icon.hpp │ ├── enum.hpp │ ├── scope_guard.hpp │ ├── rfkill.hpp │ ├── string.hpp │ ├── portal.hpp │ ├── pipewire │ │ ├── pipewire_backend.hpp │ │ └── privacy_node_info.hpp │ ├── json.hpp │ ├── css_reload_helper.hpp │ ├── regex_collection.hpp │ ├── date.hpp │ └── SafeSignal.hpp ├── IModule.hpp ├── factory.hpp ├── ASlider.hpp ├── modules │ ├── simpleclock.hpp │ ├── backlight_slider.hpp │ ├── temperature.hpp │ ├── memory.hpp │ ├── wlr │ │ └── workspace_manager_binding.hpp │ ├── cpu.hpp │ ├── disk.hpp │ ├── pulseaudio.hpp │ ├── pulseaudio_slider.hpp │ ├── sway │ │ ├── mode.hpp │ │ ├── scratchpad.hpp │ │ ├── ipc │ │ │ ├── ipc.hpp │ │ │ └── client.hpp │ │ ├── window.hpp │ │ ├── bar.hpp │ │ ├── language.hpp │ │ └── workspaces.hpp │ ├── inhibitor.hpp │ ├── load.hpp │ ├── hyprland │ │ ├── submap.hpp │ │ ├── language.hpp │ │ ├── backend.hpp │ │ └── window.hpp │ ├── river │ │ ├── mode.hpp │ │ ├── layout.hpp │ │ ├── window.hpp │ │ └── tags.hpp │ ├── sni │ │ ├── tray.hpp │ │ ├── watcher.hpp │ │ └── host.hpp │ ├── idle_inhibitor.hpp │ ├── backlight.hpp │ ├── cpu_frequency.hpp │ ├── upower │ │ └── upower_tooltip.hpp │ ├── systemd_failed_units.hpp │ ├── sndio.hpp │ ├── image.hpp │ ├── cpu_usage.hpp │ ├── dwl │ │ └── tags.hpp │ ├── user.hpp │ ├── jack.hpp │ ├── mpd │ │ ├── state.inl.hpp │ │ └── mpd.hpp │ ├── custom.hpp │ ├── privacy │ │ ├── privacy.hpp │ │ └── privacy_item.hpp │ ├── keyboard_state.hpp │ ├── wireplumber.hpp │ ├── battery.hpp │ ├── cava.hpp │ ├── cffi.hpp │ ├── network.hpp │ └── bluetooth.hpp ├── AIconLabel.hpp ├── AAppIconLabel.hpp ├── group.hpp ├── ALabel.hpp ├── config.hpp └── client.hpp ├── src ├── util │ ├── ustring_clen.cpp │ ├── sanitize_str.cpp │ ├── rewrite_string.cpp │ ├── gtk_icon.cpp │ ├── enum.cpp │ ├── prepare_for_sleep.cpp │ └── regex_collection.cpp ├── modules │ ├── cpu_frequency │ │ ├── bsd.cpp │ │ ├── linux.cpp │ │ └── common.cpp │ ├── backlight_slider.cpp │ ├── cpu_usage │ │ └── linux.cpp │ ├── memory │ │ └── linux.cpp │ ├── simpleclock.cpp │ ├── sni │ │ └── tray.cpp │ ├── sway │ │ └── mode.cpp │ ├── hyprland │ │ └── submap.cpp │ ├── load.cpp │ ├── pulseaudio_slider.cpp │ ├── image.cpp │ └── cpu.cpp ├── ASlider.cpp └── AIconLabel.cpp ├── subprojects ├── cava.wrap ├── gtk-layer-shell.wrap ├── jsoncpp.wrap ├── catch2.wrap ├── spdlog.wrap ├── date.wrap └── fmt.wrap ├── Dockerfiles ├── alpine ├── archlinux ├── opensuse ├── gentoo ├── fedora └── debian ├── default.nix ├── .editorconfig ├── Makefile ├── .gitignore ├── nix └── default.nix ├── man ├── waybar-cffi.5.scd ├── waybar-dwl-tags.5.scd ├── waybar-river-tags.5.scd ├── waybar-tray.5.scd ├── waybar-sway-language.5.scd ├── waybar-hyprland-language.5.scd ├── waybar-states.5.scd ├── waybar-river-window.5.scd ├── waybar-sway-scratchpad.5.scd ├── waybar-systemd-failed-units.5.scd ├── waybar-river-layout.5.scd ├── waybar-river-mode.5.scd ├── waybar-privacy.5.scd ├── waybar-sway-mode.5.scd ├── waybar-hyprland-submap.5.scd ├── waybar-pulseaudio-slider.5.scd ├── waybar-image.5.scd ├── waybar-backlight-slider.5.scd ├── waybar-gamemode.5.scd ├── waybar-sndio.5.scd ├── waybar-wlr-workspaces.5.scd ├── waybar-backlight.5.scd └── waybar-inhibitor.5.scd ├── LICENSE ├── flake.lock ├── .clang-tidy ├── protocol ├── dbus-status-notifier-watcher.xml └── dbus-status-notifier-item.xml ├── meson_options.txt └── flake.nix /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /resources/custom_modules/cffi_example/.gitignore: -------------------------------------------------------------------------------- 1 | .cache/ -------------------------------------------------------------------------------- /test/config/include-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "layer": "bottom" 3 | } 4 | -------------------------------------------------------------------------------- /test/config/include-multi-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "height": 21 3 | } 4 | -------------------------------------------------------------------------------- /test/config/include-multi-3-0.json: -------------------------------------------------------------------------------- 1 | { 2 | "height": 23 3 | } 4 | -------------------------------------------------------------------------------- /preview-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozlima/Waybar/master/preview-2.png -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozlima/Waybar/master/preview.png -------------------------------------------------------------------------------- /test/config/include-multi-0.json: -------------------------------------------------------------------------------- 1 | { 2 | "output": "OUT-0", 3 | "height": 20 4 | } 5 | -------------------------------------------------------------------------------- /test/config/include-multi-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "output": "OUT-1", 3 | "height": 22 4 | } 5 | -------------------------------------------------------------------------------- /test/config/simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "layer": "top", 3 | "height": 30, 4 | "output": ["HDMI-0", "DP-0"] 5 | } 6 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | AlignConsecutiveDeclarations: false 4 | ColumnLimit: 100 5 | ... 6 | -------------------------------------------------------------------------------- /test/config/include-multi-3.json: -------------------------------------------------------------------------------- 1 | { 2 | "output": "OUT-3", 3 | "include": "test/config/include-multi-3-0.json" 4 | } 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "package/archlinux"] 2 | path = package/archlinux 3 | url = https://aur.archlinux.org/waybar-git.git 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: Alexays 4 | custom: https://paypal.me/ARouillard 5 | -------------------------------------------------------------------------------- /include/util/ustring_clen.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // calculate column width of ustring 5 | int ustring_clen(const Glib::ustring &str); -------------------------------------------------------------------------------- /test/config/include.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["test/config/include-1.json", "test/config/include-2.json"], 3 | "position": "top", 4 | "nullOption": null 5 | } 6 | -------------------------------------------------------------------------------- /test/config/include-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "layer": "top", 3 | "position": "bottom", 4 | "height": 30, 5 | "output": ["HDMI-0", "DP-0"], 6 | "nullOption": "not null" 7 | } 8 | -------------------------------------------------------------------------------- /include/util/sanitize_str.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace waybar::util { 5 | std::string sanitize_string(std::string str); 6 | } // namespace waybar::util 7 | -------------------------------------------------------------------------------- /resources/icons/meson.build: -------------------------------------------------------------------------------- 1 | gnome = import('gnome') 2 | 3 | app_resources += gnome.compile_resources('icon-resources', 4 | 'waybar_icons.gresource.xml', 5 | c_name: 'waybar_icons' 6 | ) 7 | -------------------------------------------------------------------------------- /include/util/backend_common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AModule.hpp" 4 | 5 | namespace waybar::util { 6 | 7 | const static auto NOOP = []() {}; 8 | enum class ChangeType : char { Increase, Decrease }; 9 | 10 | } // namespace waybar::util -------------------------------------------------------------------------------- /src/util/ustring_clen.cpp: -------------------------------------------------------------------------------- 1 | #include "util/ustring_clen.hpp" 2 | 3 | int ustring_clen(const Glib::ustring &str) { 4 | int total = 0; 5 | for (auto i = str.begin(); i != str.end(); ++i) { 6 | total += g_unichar_iswide(*i) + 1; 7 | } 8 | return total; 9 | } -------------------------------------------------------------------------------- /include/util/prepare_for_sleep.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SafeSignal.hpp" 4 | 5 | namespace waybar::util { 6 | 7 | // Get a signal emited with value true when entering sleep, and false when exiting 8 | SafeSignal& prepare_for_sleep(); 9 | } // namespace waybar::util 10 | -------------------------------------------------------------------------------- /subprojects/cava.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = cava-0.10.1 3 | source_url = https://github.com/LukashonakV/cava/archive/0.10.1.tar.gz 4 | source_filename = cava-0.10.1.tar.gz 5 | source_hash = ae8c7339908d6febeac5ab8df4576c03c9fdbca6c8e8975daf9ce68b57038bb5 6 | [provide] 7 | cava = cava_dep 8 | -------------------------------------------------------------------------------- /subprojects/gtk-layer-shell.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = gtk-layer-shell-0.8.2 3 | source_filename = gtk-layer-shell-0.8.2.tar.gz 4 | source_hash = 254dd246303127c5d5236ea640f01a82e35d2d652a48d139dd669c832a0f0dce 5 | source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.8.2/gtk-layer-shell-0.8.2.tar.gz 6 | -------------------------------------------------------------------------------- /Dockerfiles/alpine: -------------------------------------------------------------------------------- 1 | # vim: ft=Dockerfile 2 | 3 | FROM alpine:latest 4 | 5 | RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon tzdata playerctl-dev 6 | -------------------------------------------------------------------------------- /subprojects/jsoncpp.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = jsoncpp-1.9.5 3 | source_url = https://github.com/open-source-parsers/jsoncpp/archive/1.9.5.tar.gz 4 | source_filename = jsoncpp-1.9.5.tar.gz 5 | source_hash = f409856e5920c18d0c2fb85276e24ee607d2a09b5e7d5f0a371368903c275da2 6 | 7 | [provide] 8 | jsoncpp = jsoncpp_dep 9 | 10 | -------------------------------------------------------------------------------- /resources/custom_modules/cffi_example/meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'waybar_cffi_example', 'c', 3 | version: '0.1.0', 4 | license: 'MIT', 5 | ) 6 | 7 | shared_library('wb_cffi_example', 8 | ['main.c'], 9 | dependencies: [ 10 | dependency('gtk+-3.0', version : ['>=3.22.0']) 11 | ], 12 | name_prefix: '' 13 | ) 14 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | (import 2 | ( 3 | let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in 4 | fetchTarball { 5 | url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; 6 | sha256 = lock.nodes.flake-compat.locked.narHash; 7 | } 8 | ) 9 | { src = ./.; } 10 | ).defaultNix 11 | -------------------------------------------------------------------------------- /include/IModule.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace waybar { 6 | 7 | class IModule { 8 | public: 9 | virtual ~IModule() = default; 10 | virtual auto update() -> void = 0; 11 | virtual operator Gtk::Widget&() = 0; 12 | virtual auto doAction(const std::string& name) -> void = 0; 13 | }; 14 | 15 | } // namespace waybar 16 | -------------------------------------------------------------------------------- /include/util/rewrite_string.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | 6 | namespace waybar::util { 7 | std::string rewriteString(const std::string&, const Json::Value&); 8 | std::string rewriteStringOnce(const std::string& value, const Json::Value& rules, 9 | bool& matched_any); 10 | } // namespace waybar::util 11 | -------------------------------------------------------------------------------- /test/config/include-multi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "include": "test/config/include-multi-0.json" 4 | }, 5 | { 6 | "output": "OUT-1", 7 | "include": "test/config/include-multi-1.json" 8 | }, 9 | { 10 | "output": "OUT-2", 11 | "include": "test/config/include-multi-2.json" 12 | }, 13 | { 14 | "include": "test/config/include-multi-3.json" 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /include/util/gtk_icon.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | class DefaultGtkIconThemeWrapper { 8 | private: 9 | static std::mutex default_theme_mutex; 10 | 11 | public: 12 | static bool has_icon(const std::string&); 13 | static Glib::RefPtr load_icon(const char*, int, Gtk::IconLookupFlags); 14 | }; 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig configuration for Waybar 2 | # http://EditorConfig.org 3 | 4 | # Top-most EditorConfig file 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | charset = utf-8 12 | 13 | [*.{build,css}] 14 | indent_style = space 15 | indent_size = 4 16 | 17 | [*.{hpp,cpp}] 18 | indent_style = space 19 | indent_size = 2 20 | -------------------------------------------------------------------------------- /Dockerfiles/archlinux: -------------------------------------------------------------------------------- 1 | # vim: ft=Dockerfile 2 | 3 | FROM archlinux:base-devel 4 | 5 | RUN pacman -Syu --noconfirm && \ 6 | pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw && \ 7 | sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen 8 | -------------------------------------------------------------------------------- /include/factory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace waybar { 8 | 9 | class Bar; 10 | 11 | class Factory { 12 | public: 13 | Factory(const Bar& bar, const Json::Value& config); 14 | AModule* makeModule(const std::string& name, const std::string& pos) const; 15 | 16 | private: 17 | const Bar& bar_; 18 | const Json::Value& config_; 19 | }; 20 | 21 | } // namespace waybar 22 | -------------------------------------------------------------------------------- /include/util/enum.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace waybar::util { 8 | 9 | template 10 | struct EnumParser { 11 | public: 12 | EnumParser(); 13 | ~EnumParser(); 14 | 15 | EnumType parseStringToEnum(const std::string& str, 16 | const std::map& enumMap); 17 | }; 18 | 19 | } // namespace waybar::util 20 | -------------------------------------------------------------------------------- /include/ASlider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AModule.hpp" 4 | #include "gtkmm/scale.h" 5 | 6 | namespace waybar { 7 | 8 | class ASlider : public AModule { 9 | public: 10 | ASlider(const Json::Value& config, const std::string& name, const std::string& id); 11 | virtual void onValueChanged(); 12 | 13 | protected: 14 | bool vertical_ = false; 15 | int min_ = 0, max_ = 100, curr_ = 50; 16 | Gtk::Scale scale_; 17 | }; 18 | 19 | } // namespace waybar -------------------------------------------------------------------------------- /resources/icons/waybar_icons.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | waybar-privacy-audio-input-symbolic.svg 5 | waybar-privacy-audio-output-symbolic.svg 6 | waybar-privacy-screen-share-symbolic.svg 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/waybar.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Highly customizable Wayland bar for Sway and Wlroots based compositors. 3 | Documentation=https://github.com/Alexays/Waybar/wiki/ 4 | PartOf=graphical-session.target 5 | After=graphical-session.target 6 | Requisite=graphical-session.target 7 | 8 | [Service] 9 | ExecStart=@prefix@/bin/waybar 10 | ExecReload=kill -SIGUSR2 $MAINPID 11 | Restart=on-failure 12 | 13 | [Install] 14 | WantedBy=graphical-session.target 15 | -------------------------------------------------------------------------------- /include/modules/simpleclock.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ALabel.hpp" 6 | #include "util/sleeper_thread.hpp" 7 | 8 | namespace waybar::modules { 9 | 10 | class Clock : public ALabel { 11 | public: 12 | Clock(const std::string&, const Json::Value&); 13 | virtual ~Clock() = default; 14 | auto update() -> void override; 15 | 16 | private: 17 | util::SleeperThread thread_; 18 | }; 19 | 20 | } // namespace waybar::modules 21 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Issue Labeler" 2 | on: 3 | issues: 4 | types: [opened, edited] 5 | 6 | permissions: 7 | issues: write 8 | contents: read 9 | 10 | jobs: 11 | triage: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: github/issue-labeler@v3.4 15 | with: 16 | configuration-path: .github/labeler.yml 17 | enable-versioned-regex: 0 18 | include-title: 1 19 | repo-token: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /subprojects/catch2.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = Catch2-3.5.1 3 | source_url = https://github.com/catchorg/Catch2/archive/v3.5.1.tar.gz 4 | source_filename = Catch2-3.5.1.tar.gz 5 | source_hash = 49c3ca7a68f1c8ec71307736bc6ed14fec21631707e1be9af45daf4037e75a08 6 | # source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.4.0-1/Catch2-3.4.0.tar.gz 7 | # wrapdb_version = 3.4.0-1 8 | 9 | [provide] 10 | catch2 = catch2_dep 11 | catch2-with-main = catch2_with_main_dep 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build build-debug run clean default install 2 | 3 | default: build 4 | 5 | build: 6 | meson build 7 | ninja -C build 8 | 9 | build-debug: 10 | meson build --buildtype=debug 11 | ninja -C build 12 | 13 | install: build 14 | ninja -C build install 15 | 16 | run: build 17 | ./build/waybar 18 | 19 | debug-run: build-debug 20 | ./build/waybar --log-level debug 21 | 22 | test: 23 | meson test -C build --no-rebuild --verbose --suite waybar 24 | .PHONY: test 25 | 26 | clean: 27 | rm -rf build 28 | -------------------------------------------------------------------------------- /subprojects/spdlog.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = spdlog-1.11.0 3 | source_url = https://github.com/gabime/spdlog/archive/v1.11.0.tar.gz 4 | source_filename = v1.11.0.tar.gz 5 | source_hash = ca5cae8d6cac15dae0ec63b21d6ad3530070650f68076f3a4a862ca293a858bb 6 | patch_filename = spdlog_1.11.0-2_patch.zip 7 | patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.11.0-2/get_patch 8 | patch_hash = db1364fe89502ac67f245a6c8c51290a52afd74a51eed26fa9ecb5b3443df57a 9 | wrapdb_version = 1.11.0-2 10 | 11 | [provide] 12 | spdlog = spdlog_dep 13 | -------------------------------------------------------------------------------- /.github/workflows/clang-format.yml: -------------------------------------------------------------------------------- 1 | name: clang-format 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-format-${{ github.event.pull_request.number || github.ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: DoozyX/clang-format-lint-action@v0.16.2 15 | name: clang-format 16 | with: 17 | source: "." 18 | extensions: "hpp,h,cpp,c" 19 | clangFormatVersion: 16 20 | -------------------------------------------------------------------------------- /subprojects/date.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | source_url = https://github.com/HowardHinnant/date/archive/v3.0.1.tar.gz 3 | source_filename = date-3.0.1.tar.gz 4 | source_hash = 7a390f200f0ccd207e8cff6757e04817c1a0aec3e327b006b7eb451c57ee3538 5 | directory = date-3.0.1 6 | patch_filename = hinnant-date_3.0.1-2_patch.zip 7 | patch_url = https://wrapdb.mesonbuild.com/v2/hinnant-date_3.0.1-2/get_patch 8 | patch_hash = 11b715b792609117a63310eeefc2939cc2ca26ecd4e996108335e504db58a41d 9 | wrapdb_version = 3.0.1-2 10 | 11 | [provide] 12 | tz = tz_dep 13 | date = date_dep 14 | 15 | -------------------------------------------------------------------------------- /include/util/scope_guard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace waybar::util { 6 | 7 | template 8 | class ScopeGuard { 9 | public: 10 | explicit ScopeGuard(Func&& exit_function) : f{std::forward(exit_function)} {} 11 | ScopeGuard(const ScopeGuard&) = delete; 12 | ScopeGuard(ScopeGuard&&) = default; 13 | ScopeGuard& operator=(const ScopeGuard&) = delete; 14 | ScopeGuard& operator=(ScopeGuard&&) = default; 15 | ~ScopeGuard() { f(); } 16 | 17 | private: 18 | Func f; 19 | }; 20 | 21 | } // namespace waybar::util 22 | -------------------------------------------------------------------------------- /resources/icons/waybar-privacy-audio-input-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/config/multi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "layer": "bottom", 4 | "height": 20, 5 | "output": ["HDMI-0", "DP-0"] 6 | }, 7 | { 8 | "position": "bottom", 9 | "layer": "top", 10 | "height": 21, 11 | "output": ["DP-0"] 12 | }, 13 | { 14 | "position": "left", 15 | "layer": "overlay", 16 | "height": 22, 17 | "output": "Fake HDMI output #1" 18 | }, 19 | { 20 | "position": "right", 21 | "layer": "overlay", 22 | "height": 23, 23 | "output": "!HDMI-1" 24 | }, 25 | { 26 | "height": 24, 27 | "output": ["!HDMI-0", "!HDMI-1", "*"] 28 | } 29 | ] 30 | -------------------------------------------------------------------------------- /include/modules/backlight_slider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ASlider.hpp" 6 | #include "util/backlight_backend.hpp" 7 | 8 | namespace waybar::modules { 9 | 10 | class BacklightSlider : public ASlider { 11 | public: 12 | BacklightSlider(const std::string&, const Json::Value&); 13 | virtual ~BacklightSlider() = default; 14 | 15 | void update() override; 16 | void onValueChanged() override; 17 | 18 | private: 19 | std::chrono::milliseconds interval_; 20 | std::string preferred_device_; 21 | util::BacklightBackend backend; 22 | }; 23 | 24 | } // namespace waybar::modules -------------------------------------------------------------------------------- /include/modules/temperature.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "ALabel.hpp" 8 | #include "util/sleeper_thread.hpp" 9 | 10 | namespace waybar::modules { 11 | 12 | class Temperature : public ALabel { 13 | public: 14 | Temperature(const std::string&, const Json::Value&); 15 | virtual ~Temperature() = default; 16 | auto update() -> void override; 17 | 18 | private: 19 | float getTemperature(); 20 | bool isCritical(uint16_t); 21 | 22 | std::string file_path_; 23 | util::SleeperThread thread_; 24 | }; 25 | 26 | } // namespace waybar::modules 27 | -------------------------------------------------------------------------------- /subprojects/fmt.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = fmt-9.1.0 3 | source_url = https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz 4 | source_filename = fmt-9.1.0.tar.gz 5 | source_hash = 5dea48d1fcddc3ec571ce2058e13910a0d4a6bab4cc09a809d8b1dd1c88ae6f2 6 | patch_filename = fmt_9.1.0-2_patch.zip 7 | patch_url = https://wrapdb.mesonbuild.com/v2/fmt_9.1.0-2/get_patch 8 | patch_hash = 23e8c4829f3e63f509b5643fe6bb87cbed39eae9594c451b338475d14d051967 9 | source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_9.1.0-2/fmt-9.1.0.tar.gz 10 | wrapdb_version = 9.1.0-2 11 | 12 | [provide] 13 | fmt = fmt_dep 14 | -------------------------------------------------------------------------------- /include/util/rfkill.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace waybar::util { 9 | 10 | class Rfkill : public sigc::trackable { 11 | public: 12 | Rfkill(enum rfkill_type rfkill_type); 13 | ~Rfkill(); 14 | bool getState() const; 15 | 16 | sigc::signal on_update; 17 | 18 | private: 19 | enum rfkill_type rfkill_type_; 20 | bool state_ = false; 21 | int fd_ = -1; 22 | 23 | bool on_event(Glib::IOCondition cond); 24 | }; 25 | 26 | } // namespace waybar::util 27 | -------------------------------------------------------------------------------- /include/modules/memory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "ALabel.hpp" 9 | #include "util/sleeper_thread.hpp" 10 | 11 | namespace waybar::modules { 12 | 13 | class Memory : public ALabel { 14 | public: 15 | Memory(const std::string&, const Json::Value&); 16 | virtual ~Memory() = default; 17 | auto update() -> void override; 18 | 19 | private: 20 | void parseMeminfo(); 21 | 22 | std::unordered_map meminfo_; 23 | 24 | util::SleeperThread thread_; 25 | }; 26 | 27 | } // namespace waybar::modules 28 | -------------------------------------------------------------------------------- /include/modules/wlr/workspace_manager_binding.hpp: -------------------------------------------------------------------------------- 1 | #include "ext-workspace-unstable-v1-client-protocol.h" 2 | 3 | namespace waybar::modules::wlr { 4 | void add_registry_listener(void *data); 5 | void add_workspace_listener(zext_workspace_handle_v1 *workspace_handle, void *data); 6 | void add_workspace_group_listener(zext_workspace_group_handle_v1 *workspace_group_handle, 7 | void *data); 8 | zext_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name, 9 | uint32_t version, void *data); 10 | } // namespace waybar::modules::wlr 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | vgcore.* 4 | /.vscode 5 | /.idea 6 | /.cache 7 | *.swp 8 | packagecache 9 | /subprojects/**/ 10 | /build* 11 | /dist 12 | /meson.egg-info 13 | 14 | # Prerequisites 15 | *.d 16 | 17 | # Compiled Object files 18 | *.slo 19 | *.lo 20 | *.o 21 | *.obj 22 | 23 | # Precompiled Headers 24 | *.gch 25 | *.pch 26 | 27 | # Compiled Dynamic libraries 28 | *.so 29 | *.dylib 30 | *.dll 31 | 32 | # Fortran module files 33 | *.mod 34 | *.smod 35 | 36 | # Compiled Static libraries 37 | *.lai 38 | *.la 39 | *.a 40 | *.lib 41 | 42 | # Executables 43 | *.exe 44 | *.out 45 | *.app 46 | /.direnv/ 47 | 48 | # Nix 49 | result 50 | result-* 51 | -------------------------------------------------------------------------------- /Dockerfiles/opensuse: -------------------------------------------------------------------------------- 1 | # vim: ft=Dockerfile 2 | 3 | FROM opensuse/tumbleweed:latest 4 | 5 | RUN zypper -n up && \ 6 | zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \ 7 | zypper -n refresh && \ 8 | zypper -n install -t pattern devel_C_C++ && \ 9 | zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc playerctl-devel 10 | -------------------------------------------------------------------------------- /include/modules/cpu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "ALabel.hpp" 13 | #include "util/sleeper_thread.hpp" 14 | 15 | namespace waybar::modules { 16 | 17 | class Cpu : public ALabel { 18 | public: 19 | Cpu(const std::string&, const Json::Value&); 20 | virtual ~Cpu() = default; 21 | auto update() -> void override; 22 | 23 | private: 24 | std::vector> prev_times_; 25 | 26 | util::SleeperThread thread_; 27 | }; 28 | 29 | } // namespace waybar::modules 30 | -------------------------------------------------------------------------------- /include/modules/disk.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "ALabel.hpp" 9 | #include "util/format.hpp" 10 | #include "util/sleeper_thread.hpp" 11 | 12 | namespace waybar::modules { 13 | 14 | class Disk : public ALabel { 15 | public: 16 | Disk(const std::string&, const Json::Value&); 17 | virtual ~Disk() = default; 18 | auto update() -> void override; 19 | 20 | private: 21 | util::SleeperThread thread_; 22 | std::string path_; 23 | std::string unit_; 24 | 25 | float calc_specific_divisor(const std::string divisor); 26 | }; 27 | 28 | } // namespace waybar::modules 29 | -------------------------------------------------------------------------------- /include/AIconLabel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "ALabel.hpp" 7 | 8 | namespace waybar { 9 | 10 | class AIconLabel : public ALabel { 11 | public: 12 | AIconLabel(const Json::Value &config, const std::string &name, const std::string &id, 13 | const std::string &format, uint16_t interval = 0, bool ellipsize = false, 14 | bool enable_click = false, bool enable_scroll = false); 15 | virtual ~AIconLabel() = default; 16 | auto update() -> void override; 17 | 18 | protected: 19 | Gtk::Image image_; 20 | Gtk::Box box_; 21 | 22 | bool iconEnabled() const; 23 | }; 24 | 25 | } // namespace waybar 26 | -------------------------------------------------------------------------------- /include/modules/pulseaudio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ALabel.hpp" 10 | #include "util/audio_backend.hpp" 11 | 12 | namespace waybar::modules { 13 | 14 | class Pulseaudio : public ALabel { 15 | public: 16 | Pulseaudio(const std::string&, const Json::Value&); 17 | virtual ~Pulseaudio() = default; 18 | auto update() -> void override; 19 | 20 | private: 21 | bool handleScroll(GdkEventScroll* e) override; 22 | const std::vector getPulseIcon() const; 23 | 24 | std::shared_ptr backend = nullptr; 25 | }; 26 | 27 | } // namespace waybar::modules 28 | -------------------------------------------------------------------------------- /include/modules/pulseaudio_slider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ASlider.hpp" 6 | #include "util/audio_backend.hpp" 7 | namespace waybar::modules { 8 | 9 | enum class PulseaudioSliderTarget { 10 | Sink, 11 | Source, 12 | }; 13 | 14 | class PulseaudioSlider : public ASlider { 15 | public: 16 | PulseaudioSlider(const std::string&, const Json::Value&); 17 | virtual ~PulseaudioSlider() = default; 18 | 19 | void update() override; 20 | void onValueChanged() override; 21 | 22 | private: 23 | std::shared_ptr backend = nullptr; 24 | PulseaudioSliderTarget target = PulseaudioSliderTarget::Sink; 25 | }; 26 | 27 | } // namespace waybar::modules -------------------------------------------------------------------------------- /include/modules/sway/mode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ALabel.hpp" 6 | #include "bar.hpp" 7 | #include "client.hpp" 8 | #include "modules/sway/ipc/client.hpp" 9 | #include "util/json.hpp" 10 | 11 | namespace waybar::modules::sway { 12 | 13 | class Mode : public ALabel, public sigc::trackable { 14 | public: 15 | Mode(const std::string&, const Json::Value&); 16 | virtual ~Mode() = default; 17 | auto update() -> void override; 18 | 19 | private: 20 | void onEvent(const struct Ipc::ipc_response&); 21 | 22 | std::string mode_; 23 | util::JsonParser parser_; 24 | std::mutex mutex_; 25 | Ipc ipc_; 26 | }; 27 | 28 | } // namespace waybar::modules::sway 29 | -------------------------------------------------------------------------------- /include/modules/inhibitor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "ALabel.hpp" 8 | #include "bar.hpp" 9 | 10 | namespace waybar::modules { 11 | 12 | class Inhibitor : public ALabel { 13 | public: 14 | Inhibitor(const std::string&, const waybar::Bar&, const Json::Value&); 15 | virtual ~Inhibitor(); 16 | auto update() -> void override; 17 | auto activated() -> bool; 18 | 19 | private: 20 | auto handleToggle(::GdkEventButton* const& e) -> bool override; 21 | 22 | const std::unique_ptr<::GDBusConnection, void (*)(::GDBusConnection*)> dbus_; 23 | const std::string inhibitors_; 24 | int handle_ = -1; 25 | }; 26 | 27 | } // namespace waybar::modules 28 | -------------------------------------------------------------------------------- /include/modules/load.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "ALabel.hpp" 13 | #include "util/sleeper_thread.hpp" 14 | 15 | namespace waybar::modules { 16 | 17 | class Load : public ALabel { 18 | public: 19 | Load(const std::string&, const Json::Value&); 20 | virtual ~Load() = default; 21 | auto update() -> void override; 22 | 23 | // This is a static member because it is also used by the cpu module. 24 | static std::tuple getLoad(); 25 | 26 | private: 27 | util::SleeperThread thread_; 28 | }; 29 | 30 | } // namespace waybar::modules 31 | -------------------------------------------------------------------------------- /include/modules/hyprland/submap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "ALabel.hpp" 8 | #include "bar.hpp" 9 | #include "modules/hyprland/backend.hpp" 10 | #include "util/json.hpp" 11 | 12 | namespace waybar::modules::hyprland { 13 | 14 | class Submap : public waybar::ALabel, public EventHandler { 15 | public: 16 | Submap(const std::string&, const waybar::Bar&, const Json::Value&); 17 | ~Submap() override; 18 | 19 | auto update() -> void override; 20 | 21 | private: 22 | void onEvent(const std::string& ev) override; 23 | 24 | std::mutex mutex_; 25 | const Bar& bar_; 26 | util::JsonParser parser_; 27 | std::string submap_; 28 | }; 29 | 30 | } // namespace waybar::modules::hyprland 31 | -------------------------------------------------------------------------------- /include/modules/river/mode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ALabel.hpp" 6 | #include "bar.hpp" 7 | #include "river-status-unstable-v1-client-protocol.h" 8 | 9 | namespace waybar::modules::river { 10 | 11 | class Mode : public waybar::ALabel { 12 | public: 13 | Mode(const std::string &, const waybar::Bar &, const Json::Value &); 14 | virtual ~Mode(); 15 | 16 | // Handlers for wayland events 17 | void handle_mode(const char *mode); 18 | 19 | struct zriver_status_manager_v1 *status_manager_; 20 | struct wl_seat *seat_; 21 | 22 | private: 23 | const waybar::Bar &bar_; 24 | std::string mode_; 25 | struct zriver_seat_status_v1 *seat_status_; 26 | }; 27 | 28 | } /* namespace waybar::modules::river */ 29 | -------------------------------------------------------------------------------- /test/GlibTestsFixture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | /** 4 | * Minimal Glib application to be used for tests that require Glib main loop 5 | */ 6 | class GlibTestsFixture : public sigc::trackable { 7 | public: 8 | GlibTestsFixture() : main_loop_{Glib::MainLoop::create()} {} 9 | 10 | void setTimeout(int timeout) { 11 | Glib::signal_timeout().connect_once([]() { throw std::runtime_error("Test timed out"); }, 12 | timeout); 13 | } 14 | 15 | void run(std::function fn) { 16 | Glib::signal_idle().connect_once(fn); 17 | main_loop_->run(); 18 | } 19 | 20 | void quit() { main_loop_->quit(); } 21 | 22 | protected: 23 | Glib::RefPtr main_loop_; 24 | }; 25 | -------------------------------------------------------------------------------- /Dockerfiles/gentoo: -------------------------------------------------------------------------------- 1 | # vim: ft=Dockerfile 2 | 3 | FROM gentoo/stage3:latest 4 | 5 | RUN export FEATURES="-ipc-sandbox -network-sandbox -pid-sandbox -sandbox -usersandbox" && \ 6 | emerge --sync && \ 7 | eselect news read --quiet new 1>/dev/null 2>&1 && \ 8 | emerge --verbose --update --deep --with-bdeps=y --backtrack=30 --newuse @world && \ 9 | USE="wayland gtk3 gtk -doc X pulseaudio minimal" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols =dev-cpp/gtkmm-3.24.6 x11-libs/libxkbcommon \ 10 | x11-libs/gtk+:3 dev-libs/libdbusmenu dev-libs/libnl sys-power/upower media-libs/libpulse dev-libs/libevdev media-libs/libmpdclient \ 11 | media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl dev-libs/iniparser sci-libs/fftw 12 | -------------------------------------------------------------------------------- /test/meson.build: -------------------------------------------------------------------------------- 1 | test_inc = include_directories('../include') 2 | test_dep = [ 3 | catch2, 4 | fmt, 5 | gtkmm, 6 | jsoncpp, 7 | spdlog, 8 | ] 9 | test_src = files( 10 | 'main.cpp', 11 | 'JsonParser.cpp', 12 | 'SafeSignal.cpp', 13 | 'config.cpp', 14 | 'css_reload_helper.cpp', 15 | '../src/config.cpp', 16 | '../src/util/css_reload_helper.cpp', 17 | ) 18 | 19 | if tz_dep.found() 20 | test_dep += tz_dep 21 | test_src += files('date.cpp') 22 | endif 23 | 24 | waybar_test = executable( 25 | 'waybar_test', 26 | test_src, 27 | dependencies: test_dep, 28 | include_directories: test_inc, 29 | ) 30 | 31 | test( 32 | 'waybar', 33 | waybar_test, 34 | workdir: meson.project_source_root(), 35 | ) 36 | -------------------------------------------------------------------------------- /include/modules/sni/tray.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "AModule.hpp" 6 | #include "bar.hpp" 7 | #include "modules/sni/host.hpp" 8 | #include "modules/sni/watcher.hpp" 9 | #include "util/json.hpp" 10 | 11 | namespace waybar::modules::SNI { 12 | 13 | class Tray : public AModule { 14 | public: 15 | Tray(const std::string&, const Bar&, const Json::Value&); 16 | virtual ~Tray() = default; 17 | auto update() -> void override; 18 | 19 | private: 20 | void onAdd(std::unique_ptr& item); 21 | void onRemove(std::unique_ptr& item); 22 | 23 | static inline std::size_t nb_hosts_ = 0; 24 | Gtk::Box box_; 25 | SNI::Watcher::singleton watcher_; 26 | SNI::Host host_; 27 | }; 28 | 29 | } // namespace waybar::modules::SNI 30 | -------------------------------------------------------------------------------- /include/modules/idle_inhibitor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ALabel.hpp" 6 | #include "bar.hpp" 7 | #include "client.hpp" 8 | 9 | namespace waybar::modules { 10 | 11 | class IdleInhibitor : public ALabel { 12 | sigc::connection timeout_; 13 | 14 | public: 15 | IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&); 16 | virtual ~IdleInhibitor(); 17 | auto update() -> void override; 18 | static std::list modules; 19 | static bool status; 20 | 21 | private: 22 | bool handleToggle(GdkEventButton* const& e) override; 23 | void toggleStatus(); 24 | 25 | const Bar& bar_; 26 | struct zwp_idle_inhibitor_v1* idle_inhibitor_; 27 | int pid_; 28 | }; 29 | 30 | } // namespace waybar::modules 31 | -------------------------------------------------------------------------------- /src/modules/cpu_frequency/bsd.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "modules/cpu_frequency.hpp" 5 | 6 | std::vector waybar::modules::CpuFrequency::parseCpuFrequencies() { 7 | std::vector frequencies; 8 | char buffer[256]; 9 | size_t len; 10 | int32_t freq; 11 | uint32_t i = 0; 12 | 13 | while (true) { 14 | len = 4; 15 | snprintf(buffer, 256, "dev.cpu.%u.freq", i); 16 | if (sysctlbyname(buffer, &freq, &len, NULL, 0) == -1 || len <= 0) break; 17 | frequencies.push_back(freq); 18 | ++i; 19 | } 20 | 21 | if (frequencies.empty()) { 22 | spdlog::warn("cpu/bsd: parseCpuFrequencies failed, not found in sysctl"); 23 | frequencies.push_back(NAN); 24 | } 25 | 26 | return frequencies; 27 | } 28 | -------------------------------------------------------------------------------- /include/modules/backlight.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ALabel.hpp" 10 | #include "util/backlight_backend.hpp" 11 | #include "util/json.hpp" 12 | 13 | struct udev; 14 | struct udev_device; 15 | 16 | namespace waybar::modules { 17 | 18 | class Backlight : public ALabel { 19 | public: 20 | Backlight(const std::string &, const Json::Value &); 21 | virtual ~Backlight() = default; 22 | auto update() -> void override; 23 | 24 | bool handleScroll(GdkEventScroll *e) override; 25 | 26 | const std::string preferred_device_; 27 | 28 | std::string previous_format_; 29 | 30 | util::BacklightBackend backend; 31 | }; 32 | } // namespace waybar::modules 33 | -------------------------------------------------------------------------------- /include/modules/cpu_frequency.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "ALabel.hpp" 13 | #include "util/sleeper_thread.hpp" 14 | 15 | namespace waybar::modules { 16 | 17 | class CpuFrequency : public ALabel { 18 | public: 19 | CpuFrequency(const std::string&, const Json::Value&); 20 | virtual ~CpuFrequency() = default; 21 | auto update() -> void override; 22 | 23 | // This is a static member because it is also used by the cpu module. 24 | static std::tuple getCpuFrequency(); 25 | 26 | private: 27 | static std::vector parseCpuFrequencies(); 28 | 29 | util::SleeperThread thread_; 30 | }; 31 | 32 | } // namespace waybar::modules 33 | -------------------------------------------------------------------------------- /include/modules/upower/upower_tooltip.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "gtkmm/box.h" 9 | #include "gtkmm/label.h" 10 | #include "gtkmm/window.h" 11 | 12 | namespace waybar::modules::upower { 13 | 14 | class UPowerTooltip : public Gtk::Window { 15 | private: 16 | typedef std::unordered_map Devices; 17 | 18 | const std::string getDeviceIcon(UpDeviceKind& kind); 19 | 20 | std::unique_ptr contentBox; 21 | 22 | uint iconSize; 23 | uint tooltipSpacing; 24 | uint tooltipPadding; 25 | 26 | public: 27 | UPowerTooltip(uint iconSize, uint tooltipSpacing, uint tooltipPadding); 28 | virtual ~UPowerTooltip(); 29 | 30 | uint updateTooltip(Devices& devices); 31 | }; 32 | 33 | } // namespace waybar::modules::upower 34 | -------------------------------------------------------------------------------- /include/util/string.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | const std::string WHITESPACE = " \n\r\t\f\v"; 7 | 8 | inline std::string ltrim(const std::string& s) { 9 | size_t begin = s.find_first_not_of(WHITESPACE); 10 | return (begin == std::string::npos) ? "" : s.substr(begin); 11 | } 12 | 13 | inline std::string rtrim(const std::string& s) { 14 | size_t end = s.find_last_not_of(WHITESPACE); 15 | return (end == std::string::npos) ? "" : s.substr(0, end + 1); 16 | } 17 | 18 | inline std::string trim(const std::string& s) { return rtrim(ltrim(s)); } 19 | 20 | inline std::string capitalize(const std::string& str) { 21 | std::string result = str; 22 | std::transform(result.begin(), result.end(), result.begin(), 23 | [](unsigned char c) { return std::toupper(c); }); 24 | return result; 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: linux 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-linux-${{ github.event.pull_request.number || github.ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | distro: 14 | - alpine 15 | - archlinux 16 | - debian 17 | - fedora 18 | - opensuse 19 | - gentoo 20 | cpp_std: [c++20] 21 | 22 | runs-on: ubuntu-latest 23 | container: 24 | image: alexays/waybar:${{ matrix.distro }} 25 | 26 | steps: 27 | - uses: actions/checkout@v3 28 | - name: configure 29 | run: meson -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build 30 | - name: build 31 | run: ninja -C build 32 | - name: test 33 | run: make test 34 | -------------------------------------------------------------------------------- /include/modules/systemd_failed_units.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "ALabel.hpp" 8 | 9 | namespace waybar::modules { 10 | 11 | class SystemdFailedUnits : public ALabel { 12 | public: 13 | SystemdFailedUnits(const std::string &, const Json::Value &); 14 | virtual ~SystemdFailedUnits(); 15 | auto update() -> void override; 16 | 17 | private: 18 | bool hide_on_ok; 19 | std::string format_ok; 20 | 21 | bool update_pending; 22 | uint32_t nr_failed_system, nr_failed_user; 23 | std::string last_status; 24 | Glib::RefPtr system_proxy, user_proxy; 25 | 26 | void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name, 27 | const Glib::VariantContainerBase &arguments); 28 | void updateData(); 29 | }; 30 | 31 | } // namespace waybar::modules 32 | -------------------------------------------------------------------------------- /src/modules/backlight_slider.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/backlight_slider.hpp" 2 | 3 | #include "ASlider.hpp" 4 | 5 | namespace waybar::modules { 6 | 7 | BacklightSlider::BacklightSlider(const std::string& id, const Json::Value& config) 8 | : ASlider(config, "backlight-slider", id), 9 | interval_(config_["interval"].isUInt() ? config_["interval"].asUInt() : 1000), 10 | preferred_device_(config["device"].isString() ? config["device"].asString() : ""), 11 | backend(interval_, [this] { this->dp.emit(); }) {} 12 | 13 | void BacklightSlider::update() { 14 | uint16_t brightness = backend.get_scaled_brightness(preferred_device_); 15 | scale_.set_value(brightness); 16 | } 17 | 18 | void BacklightSlider::onValueChanged() { 19 | auto brightness = scale_.get_value(); 20 | backend.set_scaled_brightness(preferred_device_, brightness); 21 | } 22 | 23 | } // namespace waybar::modules -------------------------------------------------------------------------------- /nix/default.nix: -------------------------------------------------------------------------------- 1 | { lib 2 | , pkgs 3 | , waybar 4 | , version 5 | }: 6 | let 7 | libcava = rec { 8 | version = "0.10.1"; 9 | src = pkgs.fetchFromGitHub { 10 | owner = "LukashonakV"; 11 | repo = "cava"; 12 | rev = version; 13 | hash = "sha256-iIYKvpOWafPJB5XhDOSIW9Mb4I3A4pcgIIPQdQYEqUw="; 14 | }; 15 | }; 16 | in 17 | (waybar.overrideAttrs ( 18 | oldAttrs: { 19 | inherit version; 20 | 21 | src = lib.cleanSourceWith { 22 | filter = name: type: type != "regular" || !lib.hasSuffix ".nix" name; 23 | src = lib.cleanSource ../.; 24 | }; 25 | 26 | mesonFlags = lib.remove "-Dgtk-layer-shell=enabled" oldAttrs.mesonFlags; 27 | 28 | postUnpack = '' 29 | pushd "$sourceRoot" 30 | cp -R --no-preserve=mode,ownership ${libcava.src} subprojects/cava-${libcava.version} 31 | patchShebangs . 32 | popd 33 | ''; 34 | } 35 | )) -------------------------------------------------------------------------------- /include/AAppIconLabel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "AIconLabel.hpp" 7 | 8 | namespace waybar { 9 | 10 | class AAppIconLabel : public AIconLabel { 11 | public: 12 | AAppIconLabel(const Json::Value &config, const std::string &name, const std::string &id, 13 | const std::string &format, uint16_t interval = 0, bool ellipsize = false, 14 | bool enable_click = false, bool enable_scroll = false); 15 | virtual ~AAppIconLabel() = default; 16 | auto update() -> void override; 17 | 18 | protected: 19 | void updateAppIconName(const std::string &app_identifier, 20 | const std::string &alternative_app_identifier); 21 | void updateAppIcon(); 22 | unsigned app_icon_size_{24}; 23 | bool update_app_icon_{true}; 24 | std::string app_icon_name_; 25 | }; 26 | 27 | } // namespace waybar 28 | -------------------------------------------------------------------------------- /include/group.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "AModule.hpp" 8 | #include "gtkmm/revealer.h" 9 | 10 | namespace waybar { 11 | 12 | class Group : public AModule { 13 | public: 14 | Group(const std::string&, const std::string&, const Json::Value&, bool); 15 | virtual ~Group() = default; 16 | auto update() -> void override; 17 | operator Gtk::Widget&() override; 18 | 19 | virtual Gtk::Box& getBox(); 20 | void addWidget(Gtk::Widget& widget); 21 | 22 | bool handleMouseHover(GdkEventCrossing* const& e); 23 | 24 | protected: 25 | Gtk::Box box; 26 | Gtk::Box revealer_box; 27 | Gtk::Revealer revealer; 28 | bool is_first_widget = true; 29 | bool is_drawer = false; 30 | std::string add_class_to_drawer_children; 31 | 32 | void addHoverHandlerTo(Gtk::Widget& widget); 33 | }; 34 | 35 | } // namespace waybar 36 | -------------------------------------------------------------------------------- /include/modules/sndio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "ALabel.hpp" 8 | #include "util/sleeper_thread.hpp" 9 | 10 | namespace waybar::modules { 11 | 12 | class Sndio : public ALabel { 13 | public: 14 | Sndio(const std::string &, const Json::Value &); 15 | virtual ~Sndio(); 16 | auto update() -> void override; 17 | auto set_desc(struct sioctl_desc *, unsigned int) -> void; 18 | auto put_val(unsigned int, unsigned int) -> void; 19 | bool handleScroll(GdkEventScroll *) override; 20 | bool handleToggle(GdkEventButton *const &) override; 21 | 22 | private: 23 | auto connect_to_sndio() -> void; 24 | util::SleeperThread thread_; 25 | struct sioctl_hdl *hdl_; 26 | std::vector pfds_; 27 | unsigned int addr_; 28 | unsigned int volume_, old_volume_, maxval_; 29 | bool muted_; 30 | }; 31 | 32 | } // namespace waybar::modules 33 | -------------------------------------------------------------------------------- /include/modules/image.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "ALabel.hpp" 10 | #include "gtkmm/box.h" 11 | #include "util/command.hpp" 12 | #include "util/json.hpp" 13 | #include "util/sleeper_thread.hpp" 14 | 15 | namespace waybar::modules { 16 | 17 | class Image : public AModule { 18 | public: 19 | Image(const std::string&, const Json::Value&); 20 | virtual ~Image() = default; 21 | auto update() -> void override; 22 | void refresh(int /*signal*/) override; 23 | 24 | private: 25 | void delayWorker(); 26 | void handleEvent(); 27 | void parseOutputRaw(); 28 | 29 | Gtk::Box box_; 30 | Gtk::Image image_; 31 | std::string path_; 32 | std::string tooltip_; 33 | int size_; 34 | int interval_; 35 | util::command::res output_; 36 | 37 | util::SleeperThread thread_; 38 | }; 39 | 40 | } // namespace waybar::modules 41 | -------------------------------------------------------------------------------- /include/modules/sway/scratchpad.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "ALabel.hpp" 9 | #include "bar.hpp" 10 | #include "client.hpp" 11 | #include "modules/sway/ipc/client.hpp" 12 | #include "util/json.hpp" 13 | 14 | namespace waybar::modules::sway { 15 | class Scratchpad : public ALabel { 16 | public: 17 | Scratchpad(const std::string&, const Json::Value&); 18 | virtual ~Scratchpad() = default; 19 | auto update() -> void override; 20 | 21 | private: 22 | auto getTree() -> void; 23 | auto onCmd(const struct Ipc::ipc_response&) -> void; 24 | auto onEvent(const struct Ipc::ipc_response&) -> void; 25 | 26 | std::string tooltip_format_; 27 | bool show_empty_; 28 | bool tooltip_enabled_; 29 | std::string tooltip_text_; 30 | int count_; 31 | std::mutex mutex_; 32 | Ipc ipc_; 33 | util::JsonParser parser_; 34 | }; 35 | } // namespace waybar::modules::sway 36 | -------------------------------------------------------------------------------- /include/modules/cpu_usage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "ALabel.hpp" 13 | #include "util/sleeper_thread.hpp" 14 | 15 | namespace waybar::modules { 16 | 17 | class CpuUsage : public ALabel { 18 | public: 19 | CpuUsage(const std::string&, const Json::Value&); 20 | virtual ~CpuUsage() = default; 21 | auto update() -> void override; 22 | 23 | // This is a static member because it is also used by the cpu module. 24 | static std::tuple, std::string> getCpuUsage( 25 | std::vector>&); 26 | 27 | private: 28 | static std::vector> parseCpuinfo(); 29 | 30 | std::vector> prev_times_; 31 | 32 | util::SleeperThread thread_; 33 | }; 34 | 35 | } // namespace waybar::modules 36 | -------------------------------------------------------------------------------- /src/util/sanitize_str.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace waybar::util { 7 | // replaces ``<>&"'`` with their encoded counterparts 8 | std::string sanitize_string(std::string str) { 9 | // note: it's important that '&' is replaced first; therefore we *can't* use std::map 10 | const std::pair replacement_table[] = { 11 | {'&', "&"}, {'<', "<"}, {'>', ">"}, {'"', """}, {'\'', "'"}}; 12 | size_t startpoint; 13 | for (size_t i = 0; i < (sizeof(replacement_table) / sizeof(replacement_table[0])); ++i) { 14 | startpoint = 0; 15 | std::pair pair = replacement_table[i]; 16 | while ((startpoint = str.find(pair.first, startpoint)) != std::string::npos) { 17 | str.replace(startpoint, 1, pair.second); 18 | startpoint += pair.second.length(); 19 | } 20 | } 21 | 22 | return str; 23 | } 24 | } // namespace waybar::util 25 | -------------------------------------------------------------------------------- /man/waybar-cffi.5.scd: -------------------------------------------------------------------------------- 1 | waybar-cffi(5) 2 | # NAME 3 | 4 | waybar - cffi module 5 | 6 | # DESCRIPTION 7 | 8 | The *cffi* module gives full control of a GTK widget to a third-party dynamic library, to create more complex modules using different programming languages. 9 | 10 | # CONFIGURATION 11 | 12 | Addressed by *cffi/* 13 | 14 | *module_path*: ++ 15 | typeof: string ++ 16 | The path to the dynamic library to load to control the widget. 17 | 18 | Some additional configuration may be required depending on the cffi dynamic library being used. 19 | 20 | 21 | # EXAMPLES 22 | 23 | ## C example: 24 | 25 | An example module written in C can be found at https://github.com/Alexays/Waybar/resources/custom_modules/cffi_example/ 26 | 27 | Waybar config to enable the module: 28 | ``` 29 | "cffi/c_example": { 30 | "module_path": ".config/waybar/cffi/wb_cffi_example.so" 31 | } 32 | ``` 33 | 34 | 35 | # STYLE 36 | 37 | The classes and IDs are managed by the cffi dynamic library. 38 | -------------------------------------------------------------------------------- /include/modules/hyprland/language.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "ALabel.hpp" 8 | #include "bar.hpp" 9 | #include "modules/hyprland/backend.hpp" 10 | #include "util/json.hpp" 11 | 12 | namespace waybar::modules::hyprland { 13 | 14 | class Language : public waybar::ALabel, public EventHandler { 15 | public: 16 | Language(const std::string&, const waybar::Bar&, const Json::Value&); 17 | virtual ~Language(); 18 | 19 | auto update() -> void override; 20 | 21 | private: 22 | void onEvent(const std::string&) override; 23 | 24 | void initLanguage(); 25 | 26 | struct Layout { 27 | std::string full_name; 28 | std::string short_name; 29 | std::string variant; 30 | std::string short_description; 31 | }; 32 | 33 | static auto getLayout(const std::string&) -> Layout; 34 | 35 | std::mutex mutex_; 36 | const Bar& bar_; 37 | util::JsonParser parser_; 38 | 39 | Layout layout_; 40 | }; 41 | 42 | } // namespace waybar::modules::hyprland 43 | -------------------------------------------------------------------------------- /src/util/rewrite_string.cpp: -------------------------------------------------------------------------------- 1 | #include "util/rewrite_string.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace waybar::util { 8 | std::string rewriteString(const std::string& value, const Json::Value& rules) { 9 | if (!rules.isObject()) { 10 | return value; 11 | } 12 | 13 | std::string res = value; 14 | 15 | for (auto it = rules.begin(); it != rules.end(); ++it) { 16 | if (it.key().isString() && it->isString()) { 17 | try { 18 | // malformated regexes will cause an exception. 19 | // in this case, log error and try the next rule. 20 | const std::regex rule{it.key().asString(), std::regex_constants::icase}; 21 | if (std::regex_match(value, rule)) { 22 | res = std::regex_replace(res, rule, it->asString()); 23 | } 24 | } catch (const std::regex_error& e) { 25 | spdlog::error("Invalid rule {}: {}", it.key().asString(), e.what()); 26 | } 27 | } 28 | } 29 | 30 | return res; 31 | } 32 | } // namespace waybar::util 33 | -------------------------------------------------------------------------------- /resources/custom_modules/cffi_example/README.md: -------------------------------------------------------------------------------- 1 | # C FFI module 2 | 3 | A C FFI module is a dynamic library that exposes standard C functions and 4 | constants, that Waybar can load and execute to create custom advanced widgets. 5 | 6 | Most language can implement the required functions and constants (C, C++, Rust, 7 | Go, Python, ...), meaning you can develop custom modules using your language of 8 | choice, as long as there's GTK bindings. 9 | 10 | Symbols to implement are documented in the 11 | [waybar_cffi_module.h](waybar_cffi_module.h) file. 12 | 13 | # Usage 14 | 15 | ## Building this module 16 | 17 | ```bash 18 | meson setup build 19 | meson compile -C build 20 | ``` 21 | 22 | ## Load the module 23 | 24 | Edit your waybar config: 25 | ```json 26 | { 27 | // ... 28 | "modules-center": [ 29 | // ... 30 | "cffi/c_example" 31 | ], 32 | // ... 33 | "cffi/c_example": { 34 | // Path to the compiled dynamic library file 35 | "module_path": "resources/custom_modules/cffi_example/build/wb_cffi_example.so" 36 | } 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /include/modules/dwl/tags.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "AModule.hpp" 7 | #include "bar.hpp" 8 | #include "dwl-ipc-unstable-v2-client-protocol.h" 9 | #include "xdg-output-unstable-v1-client-protocol.h" 10 | 11 | namespace waybar::modules::dwl { 12 | 13 | class Tags : public waybar::AModule { 14 | public: 15 | Tags(const std::string &, const waybar::Bar &, const Json::Value &); 16 | virtual ~Tags(); 17 | 18 | // Handlers for wayland events 19 | void handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused); 20 | 21 | void handle_primary_clicked(uint32_t tag); 22 | bool handle_button_press(GdkEventButton *event_button, uint32_t tag); 23 | 24 | struct zdwl_ipc_manager_v2 *status_manager_; 25 | struct wl_seat *seat_; 26 | 27 | private: 28 | const waybar::Bar &bar_; 29 | Gtk::Box box_; 30 | std::vector buttons_; 31 | struct zdwl_ipc_output_v2 *output_status_; 32 | }; 33 | 34 | } /* namespace waybar::modules::dwl */ 35 | -------------------------------------------------------------------------------- /src/modules/cpu_usage/linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "modules/cpu_usage.hpp" 4 | 5 | std::vector> waybar::modules::CpuUsage::parseCpuinfo() { 6 | const std::string data_dir_ = "/proc/stat"; 7 | std::ifstream info(data_dir_); 8 | if (!info.is_open()) { 9 | throw std::runtime_error("Can't open " + data_dir_); 10 | } 11 | std::vector> cpuinfo; 12 | std::string line; 13 | while (getline(info, line)) { 14 | if (line.substr(0, 3).compare("cpu") != 0) { 15 | break; 16 | } 17 | std::stringstream sline(line.substr(5)); 18 | std::vector times; 19 | for (size_t time = 0; sline >> time; times.push_back(time)) 20 | ; 21 | 22 | size_t idle_time = 0; 23 | size_t total_time = 0; 24 | if (times.size() >= 5) { 25 | // idle + iowait 26 | idle_time = times[3] + times[4]; 27 | total_time = std::accumulate(times.begin(), times.end(), 0); 28 | } 29 | cpuinfo.emplace_back(idle_time, total_time); 30 | } 31 | return cpuinfo; 32 | } 33 | -------------------------------------------------------------------------------- /man/waybar-dwl-tags.5.scd: -------------------------------------------------------------------------------- 1 | waybar-dwl-tags(5) 2 | 3 | # NAME 4 | 5 | waybar - dwl tags module 6 | 7 | # DESCRIPTION 8 | 9 | The *tags* module displays the current state of tags in dwl. 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *dwl/tags* 14 | 15 | *num-tags*: ++ 16 | typeof: uint ++ 17 | default: 9 ++ 18 | The number of tags that should be displayed. Max 32. 19 | 20 | *tag-labels*: ++ 21 | typeof: array ++ 22 | The label to display for each tag. 23 | 24 | *disable-click*: ++ 25 | typeof: bool ++ 26 | default: false ++ 27 | If set to false, you can left-click to set focused tag. Right-click to toggle tag focus. If set to true this behaviour is disabled. 28 | 29 | # EXAMPLE 30 | 31 | ``` 32 | "dwl/tags": { 33 | "num-tags": 5 34 | } 35 | ``` 36 | 37 | # STYLE 38 | 39 | - *#tags button* 40 | - *#tags button.occupied* 41 | - *#tags button.focused* 42 | - *#tags button.urgent* 43 | 44 | Note that occupied/focused/urgent status may overlap. That is, a tag may be 45 | both occupied and focused at the same time. 46 | 47 | # SEE ALSO 48 | 49 | waybar(5), dwl(1) 50 | -------------------------------------------------------------------------------- /include/modules/hyprland/backend.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "util/json.hpp" 10 | 11 | namespace waybar::modules::hyprland { 12 | 13 | class EventHandler { 14 | public: 15 | virtual void onEvent(const std::string& ev) = 0; 16 | virtual ~EventHandler() = default; 17 | }; 18 | 19 | class IPC { 20 | public: 21 | IPC() { startIPC(); } 22 | 23 | void registerForIPC(const std::string& ev, EventHandler* ev_handler); 24 | void unregisterForIPC(EventHandler* handler); 25 | 26 | static std::string getSocket1Reply(const std::string& rq); 27 | Json::Value getSocket1JsonReply(const std::string& rq); 28 | 29 | private: 30 | void startIPC(); 31 | void parseIPC(const std::string&); 32 | 33 | std::mutex callbackMutex_; 34 | util::JsonParser parser_; 35 | std::list> callbacks_; 36 | }; 37 | 38 | inline std::unique_ptr gIPC; 39 | inline bool modulesReady = false; 40 | }; // namespace waybar::modules::hyprland 41 | -------------------------------------------------------------------------------- /include/ALabel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "AModule.hpp" 8 | 9 | namespace waybar { 10 | 11 | class ALabel : public AModule { 12 | public: 13 | ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format, 14 | uint16_t interval = 0, bool ellipsize = false, bool enable_click = false, 15 | bool enable_scroll = false); 16 | virtual ~ALabel() = default; 17 | auto update() -> void override; 18 | virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0); 19 | virtual std::string getIcon(uint16_t, const std::vector &alts, uint16_t max = 0); 20 | 21 | protected: 22 | Gtk::Label label_; 23 | std::string format_; 24 | const std::chrono::seconds interval_; 25 | bool alt_ = false; 26 | std::string default_format_; 27 | 28 | bool handleToggle(GdkEventButton *const &e) override; 29 | virtual std::string getState(uint8_t value, bool lesser = false); 30 | }; 31 | 32 | } // namespace waybar 33 | -------------------------------------------------------------------------------- /man/waybar-river-tags.5.scd: -------------------------------------------------------------------------------- 1 | waybar-river-tags(5) 2 | 3 | # NAME 4 | 5 | waybar - river tags module 6 | 7 | # DESCRIPTION 8 | 9 | The *tags* module displays the current state of tags in river. 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *river/tags* 14 | 15 | *num-tags*: ++ 16 | typeof: uint ++ 17 | default: 9 ++ 18 | The number of tags that should be displayed. Max 32. 19 | 20 | *tag-labels*: ++ 21 | typeof: array ++ 22 | The label to display for each tag. 23 | 24 | *disable-click*: ++ 25 | typeof: bool ++ 26 | default: false ++ 27 | If set to false, you can left-click to set focused tag. Right-click to toggle tag focus. If set to true this behaviour is disabled. 28 | 29 | # EXAMPLE 30 | 31 | ``` 32 | "river/tags": { 33 | "num-tags": 5 34 | } 35 | ``` 36 | 37 | # STYLE 38 | 39 | - *#tags button* 40 | - *#tags button.occupied* 41 | - *#tags button.focused* 42 | - *#tags button.urgent* 43 | 44 | Note that occupied/focused/urgent status may overlap. That is, a tag may be 45 | both occupied and focused at the same time. 46 | 47 | # SEE ALSO 48 | 49 | waybar(5), river(1) 50 | -------------------------------------------------------------------------------- /include/modules/river/layout.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ALabel.hpp" 6 | #include "bar.hpp" 7 | #include "river-status-unstable-v1-client-protocol.h" 8 | 9 | namespace waybar::modules::river { 10 | 11 | class Layout : public waybar::ALabel { 12 | public: 13 | Layout(const std::string &, const waybar::Bar &, const Json::Value &); 14 | virtual ~Layout(); 15 | 16 | // Handlers for wayland events 17 | void handle_name(const char *name); 18 | void handle_clear(); 19 | void handle_focused_output(struct wl_output *output); 20 | void handle_unfocused_output(struct wl_output *output); 21 | 22 | struct zriver_status_manager_v1 *status_manager_; 23 | struct wl_seat *seat_; 24 | 25 | private: 26 | const waybar::Bar &bar_; 27 | struct wl_output *output_; // stores the output this module belongs to 28 | struct wl_output *focused_output_; // stores the currently focused output 29 | struct zriver_output_status_v1 *output_status_; 30 | struct zriver_seat_status_v1 *seat_status_; 31 | }; 32 | 33 | } /* namespace waybar::modules::river */ 34 | -------------------------------------------------------------------------------- /Dockerfiles/fedora: -------------------------------------------------------------------------------- 1 | # vim: ft=Dockerfile 2 | 3 | FROM fedora:latest 4 | 5 | RUN dnf install -y @c-development \ 6 | git-core glibc-langpack-en meson scdoc \ 7 | 'pkgconfig(catch2)' \ 8 | 'pkgconfig(date)' \ 9 | 'pkgconfig(dbusmenu-gtk3-0.4)' \ 10 | 'pkgconfig(fmt)' \ 11 | 'pkgconfig(gdk-pixbuf-2.0)' \ 12 | 'pkgconfig(gio-unix-2.0)' \ 13 | 'pkgconfig(gtk-layer-shell-0)' \ 14 | 'pkgconfig(gtkmm-3.0)' \ 15 | 'pkgconfig(jack)' \ 16 | 'pkgconfig(jsoncpp)' \ 17 | 'pkgconfig(libevdev)' \ 18 | 'pkgconfig(libinput)' \ 19 | 'pkgconfig(libmpdclient)' \ 20 | 'pkgconfig(libnl-3.0)' \ 21 | 'pkgconfig(libnl-genl-3.0)' \ 22 | 'pkgconfig(libpulse)' \ 23 | 'pkgconfig(libudev)' \ 24 | 'pkgconfig(playerctl)' \ 25 | 'pkgconfig(pugixml)' \ 26 | 'pkgconfig(sigc++-2.0)' \ 27 | 'pkgconfig(spdlog)' \ 28 | 'pkgconfig(upower-glib)' \ 29 | 'pkgconfig(wayland-client)' \ 30 | 'pkgconfig(wayland-cursor)' \ 31 | 'pkgconfig(wayland-protocols)' \ 32 | 'pkgconfig(wireplumber-0.4)' \ 33 | 'pkgconfig(xkbregistry)' && \ 34 | dnf clean all -y 35 | -------------------------------------------------------------------------------- /include/modules/user.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "AIconLabel.hpp" 8 | #include "util/sleeper_thread.hpp" 9 | 10 | namespace waybar::modules { 11 | class User : public AIconLabel { 12 | public: 13 | User(const std::string&, const Json::Value&); 14 | virtual ~User() = default; 15 | auto update() -> void override; 16 | 17 | bool handleToggle(GdkEventButton* const& e) override; 18 | 19 | private: 20 | util::SleeperThread thread_; 21 | 22 | static constexpr inline int defaultUserImageWidth_ = 20; 23 | static constexpr inline int defaultUserImageHeight_ = 20; 24 | 25 | long uptime_as_seconds(); 26 | std::string get_user_login() const; 27 | std::string get_user_home_dir() const; 28 | std::string get_default_user_avatar_path() const; 29 | void init_default_user_avatar(int width, int height); 30 | void init_user_avatar(const std::string& path, int width, int height); 31 | void init_avatar(const Json::Value& config); 32 | void init_update_worker(); 33 | }; 34 | } // namespace waybar::modules 35 | -------------------------------------------------------------------------------- /include/modules/river/window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "ALabel.hpp" 7 | #include "bar.hpp" 8 | #include "river-status-unstable-v1-client-protocol.h" 9 | #include "xdg-output-unstable-v1-client-protocol.h" 10 | 11 | namespace waybar::modules::river { 12 | 13 | class Window : public waybar::ALabel { 14 | public: 15 | Window(const std::string &, const waybar::Bar &, const Json::Value &); 16 | virtual ~Window(); 17 | 18 | // Handlers for wayland events 19 | void handle_focused_view(const char *title); 20 | void handle_focused_output(struct wl_output *output); 21 | void handle_unfocused_output(struct wl_output *output); 22 | 23 | struct zriver_status_manager_v1 *status_manager_; 24 | struct wl_seat *seat_; 25 | 26 | private: 27 | const waybar::Bar &bar_; 28 | struct wl_output *output_; // stores the output this module belongs to 29 | struct wl_output *focused_output_; // stores the currently focused output 30 | struct zriver_seat_status_v1 *seat_status_; 31 | }; 32 | 33 | } /* namespace waybar::modules::river */ 34 | -------------------------------------------------------------------------------- /include/modules/jack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "ALabel.hpp" 10 | #include "util/sleeper_thread.hpp" 11 | 12 | namespace waybar::modules { 13 | 14 | class JACK : public ALabel { 15 | public: 16 | JACK(const std::string &, const Json::Value &); 17 | virtual ~JACK() = default; 18 | auto update() -> void override; 19 | 20 | int bufSize(jack_nframes_t size); 21 | int sampleRate(jack_nframes_t rate); 22 | int xrun(); 23 | void shutdown(); 24 | 25 | private: 26 | std::string JACKState(); 27 | 28 | jack_client_t *client_; 29 | jack_nframes_t bufsize_; 30 | jack_nframes_t samplerate_; 31 | unsigned int xruns_; 32 | float load_; 33 | bool running_; 34 | std::mutex mutex_; 35 | std::string state_; 36 | util::SleeperThread thread_; 37 | }; 38 | 39 | } // namespace waybar::modules 40 | 41 | int bufSizeCallback(jack_nframes_t size, void *obj); 42 | int sampleRateCallback(jack_nframes_t rate, void *obj); 43 | int xrunCallback(void *obj); 44 | void shutdownCallback(void *obj); 45 | -------------------------------------------------------------------------------- /include/util/portal.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "fmt/format.h" 6 | 7 | namespace waybar { 8 | 9 | using namespace Gio; 10 | 11 | enum class Appearance { 12 | UNKNOWN = 0, 13 | DARK = 1, 14 | LIGHT = 2, 15 | }; 16 | class Portal : private DBus::Proxy { 17 | public: 18 | Portal(); 19 | void refreshAppearance(); 20 | Appearance getAppearance(); 21 | 22 | typedef sigc::signal type_signal_appearance_changed; 23 | type_signal_appearance_changed signal_appearance_changed() { return m_signal_appearance_changed; } 24 | 25 | private: 26 | type_signal_appearance_changed m_signal_appearance_changed; 27 | Appearance currentMode; 28 | void on_signal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, 29 | const Glib::VariantContainerBase& parameters); 30 | }; 31 | 32 | } // namespace waybar 33 | 34 | template <> 35 | struct fmt::formatter : formatter { 36 | // parse is inherited from formatter. 37 | auto format(waybar::Appearance c, format_context& ctx) const; 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /man/waybar-tray.5.scd: -------------------------------------------------------------------------------- 1 | waybar-tray(5) 2 | 3 | # NAME 4 | 5 | waybar - tray module 6 | 7 | # DESCRIPTION 8 | 9 | _WARNING_ *tray* is still in beta. There may be bugs. Breaking changes may occur. 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *tray* 14 | 15 | *icon-size*: ++ 16 | typeof: integer ++ 17 | Defines the size of the tray icons. 18 | 19 | *show-passive-items*: ++ 20 | typeof: bool ++ 21 | default: false ++ 22 | Defines visibility of the tray icons with *Passive* status. 23 | 24 | *smooth-scrolling-threshold*: ++ 25 | typeof: double ++ 26 | Threshold to be used when scrolling. 27 | 28 | *spacing*: ++ 29 | typeof: integer ++ 30 | Defines the spacing between the tray icons. 31 | 32 | *reverse-direction*: ++ 33 | typeof: bool ++ 34 | Defines if new app icons should be added in a reverse order 35 | 36 | *on-update*: ++ 37 | typeof: string ++ 38 | Command to execute when the module is updated. 39 | 40 | # EXAMPLES 41 | 42 | ``` 43 | "tray": { 44 | "icon-size": 21, 45 | "spacing": 10 46 | } 47 | 48 | ``` 49 | 50 | # STYLE 51 | 52 | - *#tray* 53 | - *#tray > .passive* 54 | - *#tray > .active* 55 | - *#tray > .needs-attention* 56 | -------------------------------------------------------------------------------- /src/ASlider.cpp: -------------------------------------------------------------------------------- 1 | #include "ASlider.hpp" 2 | 3 | #include "gtkmm/adjustment.h" 4 | #include "gtkmm/enums.h" 5 | 6 | namespace waybar { 7 | 8 | ASlider::ASlider(const Json::Value& config, const std::string& name, const std::string& id) 9 | : AModule(config, name, id, false, false), 10 | vertical_(config_["orientation"].asString() == "vertical"), 11 | scale_(vertical_ ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL) { 12 | scale_.set_name(name); 13 | if (!id.empty()) { 14 | scale_.get_style_context()->add_class(id); 15 | } 16 | scale_.get_style_context()->add_class(MODULE_CLASS); 17 | event_box_.add(scale_); 18 | scale_.signal_value_changed().connect(sigc::mem_fun(*this, &ASlider::onValueChanged)); 19 | 20 | if (config_["min"].isUInt()) { 21 | min_ = config_["min"].asUInt(); 22 | } 23 | 24 | if (config_["max"].isUInt()) { 25 | max_ = config_["max"].asUInt(); 26 | } 27 | 28 | scale_.set_inverted(vertical_); 29 | scale_.set_draw_value(false); 30 | scale_.set_adjustment(Gtk::Adjustment::create(curr_, min_, max_ + 1, 1, 1, 1)); 31 | } 32 | 33 | void ASlider::onValueChanged() {} 34 | 35 | } // namespace waybar -------------------------------------------------------------------------------- /include/modules/mpd/state.inl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace detail { 4 | 5 | inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; } 6 | inline bool Context::is_playing() const { return mpd_module_->playing(); } 7 | inline bool Context::is_paused() const { return mpd_module_->paused(); } 8 | inline bool Context::is_stopped() const { return mpd_module_->stopped(); } 9 | 10 | constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_.count(); } 11 | inline void Context::tryConnect() const { mpd_module_->tryConnect(); } 12 | inline unique_connection& Context::connection() { return mpd_module_->connection_; } 13 | constexpr inline mpd_state Context::state() const { return mpd_module_->state_; } 14 | 15 | inline void Context::do_update() { mpd_module_->setLabel(); } 16 | 17 | inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); } 18 | inline void Context::queryMPD() const { mpd_module_->queryMPD(); } 19 | inline void Context::fetchState() const { mpd_module_->fetchState(); } 20 | inline void Context::emit() const { mpd_module_->emit(); } 21 | 22 | } // namespace detail 23 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | bug: 2 | - "(crash|bug|error|coredump|freeze|segfault|issue|problem)" 3 | 4 | enhancement: 5 | - "(feature|enhancement|improvement|request|suggestion)" 6 | 7 | hyprland: 8 | - "(hyprland)" 9 | 10 | network: 11 | - "(network|wifi|ethernet)" 12 | 13 | bluetooth: 14 | - "(bluetooth|bluez)" 15 | 16 | sway: 17 | - "(sway)" 18 | 19 | cpu: 20 | - "(cpu)" 21 | 22 | memory: 23 | - "(memory|ram)" 24 | 25 | disk: 26 | - "(disk|storage)" 27 | 28 | battery: 29 | - "(upower|battery)" 30 | 31 | sni: 32 | - "(sni|tray)" 33 | 34 | dwl: 35 | - "(dwl)" 36 | 37 | custom: 38 | - "(custom|module|extension|plugin|script)" 39 | 40 | mpd: 41 | - "(mpd|music)" 42 | 43 | audio: 44 | - "(pulseaudio|alsa|jack|audio|pirewire|wireplumber)" 45 | 46 | temperature: 47 | - "(temperature|thermal|hwmon)" 48 | 49 | clock: 50 | - "(clock|time|date)" 51 | 52 | gamemode: 53 | - "(gamemode|game|gaming)" 54 | 55 | inhibitor: 56 | - "(inhibitor|idle|lock|suspend|hibernate|logout)" 57 | 58 | cava: 59 | - "(cava|audio-visualizer)" 60 | 61 | backlight: 62 | - "(backlight|brightness)" 63 | 64 | keyboard: 65 | - "(keyboard|keymap|layout|shortcut)" 66 | -------------------------------------------------------------------------------- /include/config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #ifndef SYSCONFDIR 9 | #define SYSCONFDIR "/etc" 10 | #endif 11 | 12 | namespace waybar { 13 | 14 | class Config { 15 | public: 16 | static const std::vector CONFIG_DIRS; 17 | static const char *CONFIG_PATH_ENV; 18 | 19 | /* Try to find any of provided names in the supported set of config directories */ 20 | static std::optional findConfigPath( 21 | const std::vector &names, const std::vector &dirs = CONFIG_DIRS); 22 | 23 | Config() = default; 24 | 25 | void load(const std::string &config); 26 | 27 | Json::Value &getConfig() { return config_; } 28 | 29 | std::vector getOutputConfigs(const std::string &name, const std::string &identifier); 30 | 31 | private: 32 | void setupConfig(Json::Value &dst, const std::string &config_file, int depth); 33 | void resolveConfigIncludes(Json::Value &config, int depth); 34 | void mergeConfig(Json::Value &a_config_, Json::Value &b_config_); 35 | 36 | std::string config_file_; 37 | 38 | Json::Value config_; 39 | }; 40 | } // namespace waybar 41 | -------------------------------------------------------------------------------- /include/modules/sway/ipc/ipc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define event_mask(ev) (1u << (ev & 0x7F)) 6 | 7 | enum ipc_command_type : uint32_t { 8 | // i3 command types - see i3's I3_REPLY_TYPE constants 9 | IPC_COMMAND = 0, 10 | IPC_GET_WORKSPACES = 1, 11 | IPC_SUBSCRIBE = 2, 12 | IPC_GET_OUTPUTS = 3, 13 | IPC_GET_TREE = 4, 14 | IPC_GET_MARKS = 5, 15 | IPC_GET_BAR_CONFIG = 6, 16 | IPC_GET_VERSION = 7, 17 | IPC_GET_BINDING_MODES = 8, 18 | IPC_GET_CONFIG = 9, 19 | IPC_SEND_TICK = 10, 20 | 21 | // sway-specific command types 22 | IPC_GET_INPUTS = 100, 23 | IPC_GET_SEATS = 101, 24 | 25 | // Events sent from sway to clients. Events have the highest bits set. 26 | IPC_EVENT_WORKSPACE = ((1U << 31) | 0), 27 | IPC_EVENT_OUTPUT = ((1U << 31) | 1), 28 | IPC_EVENT_MODE = ((1U << 31) | 2), 29 | IPC_EVENT_WINDOW = ((1U << 31) | 3), 30 | IPC_EVENT_BARCONFIG_UPDATE = ((1U << 31) | 4), 31 | IPC_EVENT_BINDING = ((1U << 31) | 5), 32 | IPC_EVENT_SHUTDOWN = ((1U << 31) | 6), 33 | IPC_EVENT_TICK = ((1U << 31) | 7), 34 | 35 | // sway-specific event types 36 | IPC_EVENT_BAR_STATE_UPDATE = ((1U << 31) | 20), 37 | IPC_EVENT_INPUT = ((1U << 31) | 21), 38 | }; 39 | -------------------------------------------------------------------------------- /include/modules/river/tags.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "AModule.hpp" 7 | #include "bar.hpp" 8 | #include "river-control-unstable-v1-client-protocol.h" 9 | #include "river-status-unstable-v1-client-protocol.h" 10 | #include "xdg-output-unstable-v1-client-protocol.h" 11 | 12 | namespace waybar::modules::river { 13 | 14 | class Tags : public waybar::AModule { 15 | public: 16 | Tags(const std::string &, const waybar::Bar &, const Json::Value &); 17 | virtual ~Tags(); 18 | 19 | // Handlers for wayland events 20 | void handle_focused_tags(uint32_t tags); 21 | void handle_view_tags(struct wl_array *tags); 22 | void handle_urgent_tags(uint32_t tags); 23 | 24 | void handle_primary_clicked(uint32_t tag); 25 | bool handle_button_press(GdkEventButton *event_button, uint32_t tag); 26 | 27 | struct zriver_status_manager_v1 *status_manager_; 28 | struct zriver_control_v1 *control_; 29 | struct wl_seat *seat_; 30 | 31 | private: 32 | const waybar::Bar &bar_; 33 | Gtk::Box box_; 34 | std::vector buttons_; 35 | struct zriver_output_status_v1 *output_status_; 36 | }; 37 | 38 | } /* namespace waybar::modules::river */ 39 | -------------------------------------------------------------------------------- /src/modules/memory/linux.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/memory.hpp" 2 | 3 | static unsigned zfsArcSize() { 4 | std::ifstream zfs_arc_stats{"/proc/spl/kstat/zfs/arcstats"}; 5 | 6 | if (zfs_arc_stats.is_open()) { 7 | std::string name; 8 | std::string type; 9 | unsigned long data{0}; 10 | 11 | std::string line; 12 | while (std::getline(zfs_arc_stats, line)) { 13 | std::stringstream(line) >> name >> type >> data; 14 | 15 | if (name == "size") { 16 | return data / 1024; // convert to kB 17 | } 18 | } 19 | } 20 | 21 | return 0; 22 | } 23 | 24 | void waybar::modules::Memory::parseMeminfo() { 25 | const std::string data_dir_ = "/proc/meminfo"; 26 | std::ifstream info(data_dir_); 27 | if (!info.is_open()) { 28 | throw std::runtime_error("Can't open " + data_dir_); 29 | } 30 | std::string line; 31 | while (getline(info, line)) { 32 | auto posDelim = line.find(':'); 33 | if (posDelim == std::string::npos) { 34 | continue; 35 | } 36 | 37 | std::string name = line.substr(0, posDelim); 38 | int64_t value = std::stol(line.substr(posDelim + 1)); 39 | meminfo_[name] = value; 40 | } 41 | 42 | meminfo_["zfs_size"] = zfsArcSize(); 43 | } 44 | -------------------------------------------------------------------------------- /src/util/gtk_icon.cpp: -------------------------------------------------------------------------------- 1 | #include "util/gtk_icon.hpp" 2 | 3 | /* We need a global mutex for accessing the object returned by Gtk::IconTheme::get_default() 4 | * because it always returns the same object across different threads, and concurrent 5 | * access can cause data corruption and lead to invalid memory access and crashes. 6 | * Even concurrent calls that seem read only such as has_icon can cause issues because 7 | * the GTK lib may update the internal icon cache on this calls. 8 | */ 9 | 10 | std::mutex DefaultGtkIconThemeWrapper::default_theme_mutex; 11 | 12 | bool DefaultGtkIconThemeWrapper::has_icon(const std::string& value) { 13 | const std::lock_guard lock(default_theme_mutex); 14 | 15 | return Gtk::IconTheme::get_default()->has_icon(value); 16 | } 17 | 18 | Glib::RefPtr DefaultGtkIconThemeWrapper::load_icon(const char* name, int tmp_size, 19 | Gtk::IconLookupFlags flags) { 20 | const std::lock_guard lock(default_theme_mutex); 21 | 22 | auto default_theme = Gtk::IconTheme::get_default(); 23 | default_theme->rescan_if_needed(); 24 | return default_theme->load_icon(name, tmp_size, flags); 25 | } 26 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_RUNNER 2 | #include 3 | #include 4 | #include 5 | 6 | #if __has_include() 7 | #include 8 | #include 9 | #include 10 | #else 11 | #include 12 | #include 13 | #endif 14 | #include 15 | 16 | int main(int argc, char* argv[]) { 17 | Catch::Session session; 18 | Glib::init(); 19 | 20 | session.applyCommandLine(argc, argv); 21 | const auto logger = spdlog::default_logger(); 22 | #if CATCH_VERSION_MAJOR >= 3 23 | for (const auto& spec : session.config().getReporterSpecs()) { 24 | const auto& reporter_name = spec.name(); 25 | #else 26 | { 27 | const auto& reporter_name = session.config().getReporterName(); 28 | #endif 29 | if (reporter_name == "tap") { 30 | spdlog::set_pattern("# [%l] %v"); 31 | } else if (reporter_name == "compact") { 32 | logger->sinks().clear(); 33 | } else { 34 | logger->sinks().assign({std::make_shared()}); 35 | } 36 | } 37 | 38 | return session.run(); 39 | } 40 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-compat": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1696426674, 7 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 8 | "owner": "edolstra", 9 | "repo": "flake-compat", 10 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "edolstra", 15 | "repo": "flake-compat", 16 | "type": "github" 17 | } 18 | }, 19 | "nixpkgs": { 20 | "locked": { 21 | "lastModified": 1704538339, 22 | "narHash": "sha256-1734d3mQuux9ySvwf6axRWZRBhtcZA9Q8eftD6EZg6U=", 23 | "owner": "NixOS", 24 | "repo": "nixpkgs", 25 | "rev": "46ae0210ce163b3cba6c7da08840c1d63de9c701", 26 | "type": "github" 27 | }, 28 | "original": { 29 | "owner": "NixOS", 30 | "ref": "nixos-unstable", 31 | "repo": "nixpkgs", 32 | "type": "github" 33 | } 34 | }, 35 | "root": { 36 | "inputs": { 37 | "flake-compat": "flake-compat", 38 | "nixpkgs": "nixpkgs" 39 | } 40 | } 41 | }, 42 | "root": "root", 43 | "version": 7 44 | } 45 | -------------------------------------------------------------------------------- /include/util/pipewire/pipewire_backend.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "util/backend_common.hpp" 6 | #include "util/pipewire/privacy_node_info.hpp" 7 | 8 | namespace waybar::util::PipewireBackend { 9 | 10 | class PipewireBackend { 11 | private: 12 | pw_thread_loop* mainloop_; 13 | pw_context* context_; 14 | pw_core* core_; 15 | 16 | spa_hook registry_listener; 17 | 18 | /* Hack to keep constructor inaccessible but still public. 19 | * This is required to be able to use std::make_shared. 20 | * It is important to keep this class only accessible via a reference-counted 21 | * pointer because the destructor will manually free memory, and this could be 22 | * a problem with C++20's copy and move semantics. 23 | */ 24 | struct private_constructor_tag {}; 25 | 26 | public: 27 | std::mutex mutex_; 28 | 29 | pw_registry* registry; 30 | 31 | sigc::signal privacy_nodes_changed_signal_event; 32 | 33 | std::unordered_map privacy_nodes; 34 | 35 | static std::shared_ptr getInstance(); 36 | 37 | PipewireBackend(private_constructor_tag tag); 38 | ~PipewireBackend(); 39 | }; 40 | } // namespace waybar::util::PipewireBackend 41 | -------------------------------------------------------------------------------- /include/modules/custom.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "ALabel.hpp" 9 | #include "util/command.hpp" 10 | #include "util/json.hpp" 11 | #include "util/sleeper_thread.hpp" 12 | 13 | namespace waybar::modules { 14 | 15 | class Custom : public ALabel { 16 | public: 17 | Custom(const std::string&, const std::string&, const Json::Value&, const std::string&); 18 | virtual ~Custom(); 19 | auto update() -> void override; 20 | void refresh(int /*signal*/) override; 21 | 22 | private: 23 | void delayWorker(); 24 | void continuousWorker(); 25 | void waitingWorker(); 26 | void parseOutputRaw(); 27 | void parseOutputJson(); 28 | void handleEvent(); 29 | bool handleScroll(GdkEventScroll* e) override; 30 | bool handleToggle(GdkEventButton* const& e) override; 31 | 32 | const std::string name_; 33 | const std::string output_name_; 34 | std::string text_; 35 | std::string id_; 36 | std::string alt_; 37 | std::string tooltip_; 38 | std::vector class_; 39 | int percentage_; 40 | FILE* fp_; 41 | int pid_; 42 | util::command::res output_; 43 | util::JsonParser parser_; 44 | 45 | util::SleeperThread thread_; 46 | }; 47 | 48 | } // namespace waybar::modules 49 | -------------------------------------------------------------------------------- /include/modules/privacy/privacy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ALabel.hpp" 8 | #include "gtkmm/box.h" 9 | #include "modules/privacy/privacy_item.hpp" 10 | #include "util/pipewire/pipewire_backend.hpp" 11 | #include "util/pipewire/privacy_node_info.hpp" 12 | 13 | using waybar::util::PipewireBackend::PrivacyNodeInfo; 14 | 15 | namespace waybar::modules::privacy { 16 | 17 | class Privacy : public AModule { 18 | public: 19 | Privacy(const std::string &, const Json::Value &, const std::string &pos); 20 | auto update() -> void override; 21 | 22 | void onPrivacyNodesChanged(); 23 | 24 | private: 25 | std::list nodes_screenshare; // Screen is being shared 26 | std::list nodes_audio_in; // Application is using the microphone 27 | std::list nodes_audio_out; // Application is outputting audio 28 | 29 | std::mutex mutex_; 30 | sigc::connection visibility_conn; 31 | 32 | // Config 33 | Gtk::Box box_; 34 | uint iconSpacing = 4; 35 | uint iconSize = 20; 36 | uint transition_duration = 250; 37 | 38 | std::shared_ptr backend = nullptr; 39 | }; 40 | 41 | } // namespace waybar::modules::privacy 42 | -------------------------------------------------------------------------------- /include/util/json.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #if (FMT_VERSION >= 90000) 13 | 14 | template <> 15 | struct fmt::formatter : ostream_formatter {}; 16 | 17 | #endif 18 | 19 | namespace waybar::util { 20 | 21 | class JsonParser { 22 | public: 23 | JsonParser() = default; 24 | 25 | Json::Value parse(const std::string& jsonStr) { 26 | Json::Value root; 27 | 28 | // replace all occurrences of "\x" with "\u00", because JSON doesn't allow "\x" escape sequences 29 | std::string modifiedJsonStr = replaceHexadecimalEscape(jsonStr); 30 | 31 | std::istringstream jsonStream(modifiedJsonStr); 32 | std::string errs; 33 | if (!Json::parseFromStream(m_readerBuilder, jsonStream, &root, &errs)) { 34 | throw std::runtime_error("Error parsing JSON: " + errs); 35 | } 36 | return root; 37 | } 38 | 39 | private: 40 | Json::CharReaderBuilder m_readerBuilder; 41 | 42 | static std::string replaceHexadecimalEscape(const std::string& str) { 43 | static std::regex re("\\\\x"); 44 | return std::regex_replace(str, re, "\\u00"); 45 | } 46 | }; 47 | } // namespace waybar::util 48 | -------------------------------------------------------------------------------- /src/modules/simpleclock.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/simpleclock.hpp" 2 | 3 | #include 4 | 5 | waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) 6 | : ALabel(config, "clock", id, "{:%H:%M}", 60) { 7 | thread_ = [this] { 8 | dp.emit(); 9 | auto now = std::chrono::system_clock::now(); 10 | /* difference with projected wakeup time */ 11 | auto diff = now.time_since_epoch() % interval_; 12 | /* sleep until the next projected time */ 13 | thread_.sleep_for(interval_ - diff); 14 | }; 15 | } 16 | 17 | auto waybar::modules::Clock::update() -> void { 18 | tzset(); // Update timezone information 19 | auto now = std::chrono::system_clock::now(); 20 | auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); 21 | auto text = fmt::format(fmt::runtime(format_), localtime); 22 | label_.set_markup(text); 23 | 24 | if (tooltipEnabled()) { 25 | if (config_["tooltip-format"].isString()) { 26 | auto tooltip_format = config_["tooltip-format"].asString(); 27 | auto tooltip_text = fmt::format(fmt::runtime(tooltip_format), localtime); 28 | label_.set_tooltip_text(tooltip_text); 29 | } else { 30 | label_.set_tooltip_text(text); 31 | } 32 | } 33 | // Call parent update 34 | ALabel::update(); 35 | } 36 | -------------------------------------------------------------------------------- /include/modules/sway/window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "AAppIconLabel.hpp" 8 | #include "bar.hpp" 9 | #include "client.hpp" 10 | #include "modules/sway/ipc/client.hpp" 11 | #include "util/json.hpp" 12 | 13 | namespace waybar::modules::sway { 14 | 15 | class Window : public AAppIconLabel, public sigc::trackable { 16 | public: 17 | Window(const std::string&, const waybar::Bar&, const Json::Value&); 18 | virtual ~Window() = default; 19 | auto update() -> void override; 20 | 21 | private: 22 | void setClass(std::string classname, bool enable); 23 | void onEvent(const struct Ipc::ipc_response&); 24 | void onCmd(const struct Ipc::ipc_response&); 25 | std::tuple 26 | getFocusedNode(const Json::Value& nodes, std::string& output); 27 | void getTree(); 28 | 29 | const Bar& bar_; 30 | std::string window_; 31 | int windowId_; 32 | std::string app_id_; 33 | std::string app_class_; 34 | std::string layout_; 35 | std::string old_app_id_; 36 | std::size_t app_nb_; 37 | std::string shell_; 38 | int floating_count_; 39 | util::JsonParser parser_; 40 | std::mutex mutex_; 41 | Ipc ipc_; 42 | }; 43 | 44 | } // namespace waybar::modules::sway 45 | -------------------------------------------------------------------------------- /include/modules/keyboard_state.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "AModule.hpp" 10 | #include "bar.hpp" 11 | #include "util/sleeper_thread.hpp" 12 | 13 | extern "C" { 14 | #include 15 | #include 16 | } 17 | 18 | namespace waybar::modules { 19 | 20 | class KeyboardState : public AModule { 21 | public: 22 | KeyboardState(const std::string&, const waybar::Bar&, const Json::Value&); 23 | virtual ~KeyboardState(); 24 | auto update() -> void override; 25 | 26 | private: 27 | auto tryAddDevice(const std::string&) -> void; 28 | 29 | Gtk::Box box_; 30 | Gtk::Label numlock_label_; 31 | Gtk::Label capslock_label_; 32 | Gtk::Label scrolllock_label_; 33 | 34 | std::string numlock_format_; 35 | std::string capslock_format_; 36 | std::string scrolllock_format_; 37 | const std::chrono::seconds interval_; 38 | std::string icon_locked_; 39 | std::string icon_unlocked_; 40 | std::string devices_path_; 41 | 42 | struct libinput* libinput_; 43 | std::unordered_map libinput_devices_; 44 | std::set binding_keys; 45 | 46 | util::SleeperThread libinput_thread_, hotplug_thread_; 47 | }; 48 | 49 | } // namespace waybar::modules 50 | -------------------------------------------------------------------------------- /man/waybar-sway-language.5.scd: -------------------------------------------------------------------------------- 1 | waybar-sway-language(5) 2 | 3 | # NAME 4 | 5 | waybar - sway language module 6 | 7 | # DESCRIPTION 8 | 9 | The *language* module displays the current keyboard layout in Sway 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *sway/language* 14 | 15 | *format*: ++ 16 | typeof: string ++ 17 | default: {} ++ 18 | The format, how layout should be displayed. 19 | 20 | *hide-single-layout*: ++ 21 | typeof: bool ++ 22 | default: false ++ 23 | Defines visibility of the module if a single layout is configured 24 | 25 | *tooltip-format*: ++ 26 | typeof: string ++ 27 | default: {} ++ 28 | The format, how layout should be displayed in tooltip. 29 | 30 | *tooltip*: ++ 31 | typeof: bool ++ 32 | default: true ++ 33 | Option to disable tooltip on hover. 34 | 35 | # FORMAT REPLACEMENTS 36 | 37 | *{short}*: Short name of layout (e.g. "us"). Equals to {}. 38 | 39 | *{shortDescription}*: Short description of layout (e.g. "en"). 40 | 41 | *{long}*: Long name of layout (e.g. "English (Dvorak)"). 42 | 43 | *{variant}*: Variant of layout (e.g. "dvorak"). 44 | 45 | *{flag}*: Country flag of layout. 46 | 47 | # EXAMPLES 48 | 49 | ``` 50 | "sway/language": { 51 | "format": "{}", 52 | }, 53 | 54 | "sway/language": { 55 | "format": "{short} {variant}", 56 | } 57 | ``` 58 | 59 | # STYLE 60 | 61 | - *#language* 62 | -------------------------------------------------------------------------------- /include/modules/privacy/privacy_item.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "gtkmm/box.h" 11 | #include "gtkmm/image.h" 12 | #include "gtkmm/revealer.h" 13 | #include "util/pipewire/privacy_node_info.hpp" 14 | 15 | using waybar::util::PipewireBackend::PrivacyNodeInfo; 16 | using waybar::util::PipewireBackend::PrivacyNodeType; 17 | 18 | namespace waybar::modules::privacy { 19 | 20 | class PrivacyItem : public Gtk::Revealer { 21 | public: 22 | PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_, 23 | std::list *nodes, const std::string &pos, const uint icon_size, 24 | const uint transition_duration); 25 | 26 | enum PrivacyNodeType privacy_type; 27 | 28 | void set_in_use(bool in_use); 29 | 30 | private: 31 | std::list *nodes; 32 | 33 | sigc::connection signal_conn; 34 | 35 | Gtk::Box tooltip_window; 36 | 37 | bool init = false; 38 | bool in_use = false; 39 | 40 | // Config 41 | std::string iconName = "image-missing-symbolic"; 42 | bool tooltip = true; 43 | uint tooltipIconSize = 24; 44 | 45 | Gtk::Box box_; 46 | Gtk::Image icon_; 47 | 48 | void update_tooltip(); 49 | }; 50 | 51 | } // namespace waybar::modules::privacy 52 | -------------------------------------------------------------------------------- /src/AIconLabel.cpp: -------------------------------------------------------------------------------- 1 | #include "AIconLabel.hpp" 2 | 3 | #include 4 | 5 | namespace waybar { 6 | 7 | AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const std::string &id, 8 | const std::string &format, uint16_t interval, bool ellipsize, 9 | bool enable_click, bool enable_scroll) 10 | : ALabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) { 11 | event_box_.remove(); 12 | label_.unset_name(); 13 | label_.get_style_context()->remove_class(MODULE_CLASS); 14 | box_.get_style_context()->add_class(MODULE_CLASS); 15 | if (!id.empty()) { 16 | label_.get_style_context()->remove_class(id); 17 | box_.get_style_context()->add_class(id); 18 | } 19 | 20 | box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL); 21 | box_.set_name(name); 22 | 23 | int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : 8; 24 | box_.set_spacing(spacing); 25 | 26 | box_.add(image_); 27 | box_.add(label_); 28 | 29 | event_box_.add(box_); 30 | } 31 | 32 | auto AIconLabel::update() -> void { 33 | image_.set_visible(image_.get_visible() && iconEnabled()); 34 | ALabel::update(); 35 | } 36 | 37 | bool AIconLabel::iconEnabled() const { 38 | return config_["icon"].isBool() ? config_["icon"].asBool() : false; 39 | } 40 | 41 | } // namespace waybar 42 | -------------------------------------------------------------------------------- /Dockerfiles/debian: -------------------------------------------------------------------------------- 1 | # vim: ft=Dockerfile 2 | 3 | FROM debian:sid 4 | 5 | RUN apt update && \ 6 | apt install --no-install-recommends --no-install-suggests -y \ 7 | build-essential \ 8 | catch2 \ 9 | cmake \ 10 | git \ 11 | gobject-introspection \ 12 | libdbusmenu-gtk3-dev \ 13 | libegl1-mesa-dev \ 14 | libfmt-dev \ 15 | libgbm-dev \ 16 | libgirepository1.0-dev \ 17 | libgles2-mesa-dev \ 18 | libgtk-layer-shell-dev \ 19 | libgtkmm-3.0-dev \ 20 | libhowardhinnant-date-dev \ 21 | libiniparser-dev \ 22 | libinput-dev \ 23 | libjack-jackd2-dev \ 24 | libjsoncpp-dev \ 25 | libmpdclient-dev \ 26 | libnl-3-dev \ 27 | libnl-genl-3-dev \ 28 | libpixman-1-dev \ 29 | libplayerctl-dev \ 30 | libpugixml-dev \ 31 | libpulse-dev \ 32 | libsndio-dev \ 33 | libspdlog-dev \ 34 | libudev-dev \ 35 | libupower-glib-dev \ 36 | libwayland-dev \ 37 | libwireplumber-0.4-dev \ 38 | libxkbcommon-dev \ 39 | libxkbregistry-dev \ 40 | locales \ 41 | meson \ 42 | ninja-build \ 43 | pkg-config \ 44 | python3-pip \ 45 | python3-venv \ 46 | scdoc \ 47 | sudo \ 48 | wayland-protocols \ 49 | && apt clean 50 | -------------------------------------------------------------------------------- /man/waybar-hyprland-language.5.scd: -------------------------------------------------------------------------------- 1 | waybar-hyprland-language(5) 2 | 3 | # NAME 4 | 5 | waybar - hyprland language module 6 | 7 | # DESCRIPTION 8 | 9 | The *language* module displays the currently selected language. 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *hyprland/language* 14 | 15 | *format*: ++ 16 | typeof: string ++ 17 | default: {} ++ 18 | The format, how information should be displayed. 19 | 20 | *format-* ++ 21 | typeof: string++ 22 | Provide an alternative name to display per language where is the language of your choosing. Can be passed multiple times with multiple languages as shown by the example below. 23 | 24 | *keyboard-name*: ++ 25 | typeof: string ++ 26 | Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended. 27 | 28 | 29 | # FORMAT REPLACEMENTS 30 | 31 | *{short}*: Short name of layout (e.g. "us"). Equals to {}. 32 | 33 | *{shortDescription}*: Short description of layout (e.g. "en"). 34 | 35 | *{long}*: Long name of layout (e.g. "English (Dvorak)"). 36 | 37 | *{variant}*: Variant of layout (e.g. "dvorak"). 38 | 39 | 40 | # EXAMPLES 41 | 42 | ``` 43 | "hyprland/language": { 44 | "format": "Lang: {long}" 45 | "format-en": "AMERICA, HELL YEAH!" 46 | "format-tr": "As bayrakları" 47 | "keyboard-name": "at-translated-set-2-keyboard" 48 | } 49 | ``` 50 | 51 | # STYLE 52 | 53 | - *#language* 54 | -------------------------------------------------------------------------------- /man/waybar-states.5.scd: -------------------------------------------------------------------------------- 1 | waybar-states(5) 2 | 3 | # NAME 4 | 5 | waybar - states property 6 | 7 | # OVERVIEW 8 | 9 | Some modules support 'states' which allows percentage values to be used as styling triggers to 10 | apply a class when the value matches the declared state value. 11 | 12 | # STATES 13 | 14 | Every entry (*state*) consists of a ** (typeof: *string*) and a ** (typeof: *integer*). 15 | 16 | - The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the ** of the state. 17 | Each class gets activated when the current value is equal to or less than the configured ** for the *battery* module, or equal to or greater than the configured ** for all other modules. 18 | 19 | - Also, each state can have its own *format*. 20 | Those can be configured via *format-*, or if you want to differentiate a bit more, as *format--*. 21 | 22 | # EXAMPLE 23 | 24 | ``` 25 | "battery": { 26 | "bat": "BAT2", 27 | "interval": 60, 28 | "states": { 29 | "warning": 30, 30 | "critical": 15 31 | }, 32 | "format": "{capacity}% {icon}", 33 | "format-icons": ["", "", "", "", ""], 34 | "max-length": 25 35 | } 36 | ``` 37 | 38 | # STYLING STATES 39 | 40 | - *#battery.* 41 | - ** can be defined in the *config*. 42 | 43 | # EXAMPLE: 44 | 45 | - *#battery.warning: { background: orange; }* 46 | - *#battery.critical: { background: red; }* 47 | -------------------------------------------------------------------------------- /include/util/css_reload_helper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "giomm/file.h" 9 | #include "giomm/filemonitor.h" 10 | #include "glibmm/refptr.h" 11 | 12 | struct pollfd; 13 | 14 | namespace waybar { 15 | class CssReloadHelper { 16 | public: 17 | CssReloadHelper(std::string cssFile, std::function callback); 18 | 19 | virtual ~CssReloadHelper() = default; 20 | 21 | virtual void monitorChanges(); 22 | 23 | protected: 24 | std::vector parseImports(const std::string& cssFile); 25 | 26 | void parseImports(const std::string& cssFile, std::unordered_map& imports); 27 | 28 | void watchFiles(const std::vector& files); 29 | 30 | bool handleInotifyEvents(int fd); 31 | 32 | bool watch(int inotifyFd, pollfd* pollFd); 33 | 34 | virtual std::string getFileContents(const std::string& filename); 35 | 36 | virtual std::string findPath(const std::string& filename); 37 | 38 | void handleFileChange(Glib::RefPtr const& file, 39 | Glib::RefPtr const& other_type, 40 | Gio::FileMonitorEvent event_type); 41 | 42 | private: 43 | std::string m_cssFile; 44 | 45 | std::function m_callback; 46 | 47 | std::vector>> m_fileMonitors; 48 | }; 49 | } // namespace waybar 50 | -------------------------------------------------------------------------------- /include/modules/sway/ipc/client.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "ipc.hpp" 15 | #include "util/sleeper_thread.hpp" 16 | 17 | namespace waybar::modules::sway { 18 | 19 | class Ipc { 20 | public: 21 | Ipc(); 22 | ~Ipc(); 23 | 24 | struct ipc_response { 25 | uint32_t size; 26 | uint32_t type; 27 | std::string payload; 28 | }; 29 | 30 | sigc::signal signal_event; 31 | sigc::signal signal_cmd; 32 | 33 | void sendCmd(uint32_t type, const std::string &payload = ""); 34 | void subscribe(const std::string &payload); 35 | void handleEvent(); 36 | void setWorker(std::function &&func); 37 | 38 | protected: 39 | static inline const std::string ipc_magic_ = "i3-ipc"; 40 | static inline const size_t ipc_header_size_ = ipc_magic_.size() + 8; 41 | 42 | const std::string getSocketPath() const; 43 | int open(const std::string &) const; 44 | struct ipc_response send(int fd, uint32_t type, const std::string &payload = ""); 45 | struct ipc_response recv(int fd); 46 | 47 | int fd_; 48 | int fd_event_; 49 | std::mutex mutex_; 50 | util::SleeperThread thread_; 51 | }; 52 | 53 | } // namespace waybar::modules::sway 54 | -------------------------------------------------------------------------------- /include/modules/wireplumber.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "ALabel.hpp" 10 | 11 | namespace waybar::modules { 12 | 13 | class Wireplumber : public ALabel { 14 | public: 15 | Wireplumber(const std::string&, const Json::Value&); 16 | virtual ~Wireplumber(); 17 | auto update() -> void override; 18 | 19 | private: 20 | void loadRequiredApiModules(); 21 | void prepare(); 22 | void activatePlugins(); 23 | static void updateVolume(waybar::modules::Wireplumber* self, uint32_t id); 24 | static void updateNodeName(waybar::modules::Wireplumber* self, uint32_t id); 25 | static void onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self); 26 | static void onObjectManagerInstalled(waybar::modules::Wireplumber* self); 27 | static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id); 28 | static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self); 29 | 30 | bool handleScroll(GdkEventScroll* e) override; 31 | 32 | WpCore* wp_core_; 33 | GPtrArray* apis_; 34 | WpObjectManager* om_; 35 | WpPlugin* mixer_api_; 36 | WpPlugin* def_nodes_api_; 37 | gchar* default_node_name_; 38 | uint32_t pending_plugins_; 39 | bool muted_; 40 | double volume_; 41 | double min_step_; 42 | uint32_t node_id_{0}; 43 | std::string node_name_; 44 | }; 45 | 46 | } // namespace waybar::modules 47 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: > 2 | -*, 3 | bugprone-* 4 | misc-*, 5 | modernize-*, 6 | performance-*, 7 | portability-*, 8 | readability-*, 9 | -fuchsia-trailing-return, 10 | -readability-magic-numbers, 11 | -modernize-use-nodiscard, 12 | -modernize-use-trailing-return-type, 13 | -readability-braces-around-statements, 14 | -readability-redundant-access-specifiers, 15 | -readability-redundant-member-init, 16 | -readability-redundant-string-init, 17 | -readability-identifier-length 18 | CheckOptions: 19 | - { key: readability-identifier-naming.NamespaceCase, value: lower_case } 20 | - { key: readability-identifier-naming.ClassCase, value: CamelCase } 21 | - { key: readability-identifier-naming.StructCase, value: CamelCase } 22 | - { key: readability-identifier-naming.FunctionCase, value: camelBack } 23 | - { key: readability-identifier-naming.VariableCase, value: camelBack } 24 | - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack } 25 | - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ } 26 | - { key: readability-identifier-naming.EnumCase, value: CamelCase } 27 | - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } 28 | - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } 29 | - { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE } 30 | -------------------------------------------------------------------------------- /.github/workflows/freebsd.yml: -------------------------------------------------------------------------------- 1 | name: freebsd 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-freebsd-${{ github.event.pull_request.number || github.ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | clang: 11 | # Run actions in a FreeBSD VM on the macos-12 runner 12 | # https://github.com/actions/runner/issues/385 - for FreeBSD runner support 13 | # https://github.com/actions/virtual-environments/issues/4060 - for lack of VirtualBox on MacOS 11 runners 14 | runs-on: macos-12 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Test in FreeBSD VM 18 | uses: cross-platform-actions/action@v0.21.1 19 | timeout-minutes: 180 20 | with: 21 | operating_system: freebsd 22 | version: "13.2" 23 | environment_variables: CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib 24 | run: | 25 | sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf 26 | sudo pkg install -y git # subprojects/date 27 | sudo pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \ 28 | libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ 29 | pkgconf pulseaudio scdoc sndio spdlog wayland-protocols upower \ 30 | libinotify 31 | meson build -Dman-pages=enabled 32 | ninja -C build 33 | meson test -C build --no-rebuild --print-errorlogs --suite waybar 34 | -------------------------------------------------------------------------------- /include/modules/battery.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #if defined(__linux__) 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "ALabel.hpp" 16 | #include "bar.hpp" 17 | #include "util/sleeper_thread.hpp" 18 | 19 | namespace waybar::modules { 20 | 21 | namespace fs = std::filesystem; 22 | 23 | class Battery : public ALabel { 24 | public: 25 | Battery(const std::string&, const waybar::Bar&, const Json::Value&); 26 | virtual ~Battery(); 27 | auto update() -> void override; 28 | 29 | private: 30 | static inline const fs::path data_dir_ = "/sys/class/power_supply/"; 31 | 32 | void refreshBatteries(); 33 | void worker(); 34 | const std::string getAdapterStatus(uint8_t capacity) const; 35 | const std::tuple getInfos(); 36 | const std::string formatTimeRemaining(float hoursRemaining); 37 | void setBarClass(std::string&); 38 | 39 | int global_watch; 40 | std::map batteries_; 41 | fs::path adapter_; 42 | int battery_watch_fd_; 43 | int global_watch_fd_; 44 | std::mutex battery_list_mutex_; 45 | std::string old_status_; 46 | bool warnFirstTime_{true}; 47 | const Bar& bar_; 48 | 49 | util::SleeperThread thread_; 50 | util::SleeperThread thread_battery_update_; 51 | util::SleeperThread thread_timer_; 52 | }; 53 | 54 | } // namespace waybar::modules 55 | -------------------------------------------------------------------------------- /man/waybar-river-window.5.scd: -------------------------------------------------------------------------------- 1 | waybar-river-window(5) 2 | 3 | # NAME 4 | 5 | waybar - river window module 6 | 7 | # DESCRIPTION 8 | 9 | The *window* module displays the title of the currently focused window in river 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *river/window* 14 | 15 | *format*: ++ 16 | typeof: string ++ 17 | default: {} ++ 18 | The format, how information should be displayed. On {} data gets inserted. 19 | 20 | *rotate*: ++ 21 | typeof: integer ++ 22 | Positive value to rotate the text label. 23 | 24 | *max-length*: ++ 25 | typeof: integer ++ 26 | The maximum length in character the module should display. 27 | 28 | *min-length*: ++ 29 | typeof: integer ++ 30 | The minimum length in characters the module should accept. 31 | 32 | *align*: ++ 33 | typeof: float ++ 34 | 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. 35 | 36 | *on-click*: ++ 37 | typeof: string ++ 38 | Command to execute when clicked on the module. 39 | 40 | *on-click-middle*: ++ 41 | typeof: string ++ 42 | Command to execute when middle-clicked on the module using mousewheel. 43 | 44 | *on-click-right*: ++ 45 | typeof: string ++ 46 | Command to execute when you right-click on the module. 47 | 48 | # EXAMPLES 49 | 50 | ``` 51 | "river/window": { 52 | "format": "{}" 53 | } 54 | ``` 55 | 56 | # STYLE 57 | 58 | - *#window* 59 | - *#window.focused* Applied when the output this module's bar belongs to is focused. 60 | -------------------------------------------------------------------------------- /src/modules/sni/tray.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/sni/tray.hpp" 2 | 3 | #include 4 | 5 | namespace waybar::modules::SNI { 6 | 7 | Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) 8 | : AModule(config, "tray", id), 9 | box_(bar.orientation, 0), 10 | watcher_(SNI::Watcher::getInstance()), 11 | host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1), 12 | std::bind(&Tray::onRemove, this, std::placeholders::_1)) { 13 | box_.set_name("tray"); 14 | event_box_.add(box_); 15 | if (!id.empty()) { 16 | box_.get_style_context()->add_class(id); 17 | } 18 | box_.get_style_context()->add_class(MODULE_CLASS); 19 | if (config_["spacing"].isUInt()) { 20 | box_.set_spacing(config_["spacing"].asUInt()); 21 | } 22 | nb_hosts_ += 1; 23 | dp.emit(); 24 | } 25 | 26 | void Tray::onAdd(std::unique_ptr& item) { 27 | if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) { 28 | box_.pack_end(item->event_box); 29 | } else { 30 | box_.pack_start(item->event_box); 31 | } 32 | dp.emit(); 33 | } 34 | 35 | void Tray::onRemove(std::unique_ptr& item) { 36 | box_.remove(item->event_box); 37 | dp.emit(); 38 | } 39 | 40 | auto Tray::update() -> void { 41 | // Show tray only when items are available 42 | event_box_.set_visible(!box_.get_children().empty()); 43 | // Call parent update 44 | AModule::update(); 45 | } 46 | 47 | } // namespace waybar::modules::SNI 48 | -------------------------------------------------------------------------------- /man/waybar-sway-scratchpad.5.scd: -------------------------------------------------------------------------------- 1 | waybar-sway-scratchpad(5) 2 | 3 | # NAME 4 | 5 | waybar - sway scratchpad module 6 | 7 | # DESCRIPTION 8 | 9 | The *scratchpad* module displays the scratchpad status in Sway 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *sway/scratchpad* 14 | 15 | *format*: ++ 16 | typeof: string ++ 17 | default: {icon} {count} ++ 18 | The format, how information should be displayed. 19 | 20 | *show-empty*: ++ 21 | typeof: bool ++ 22 | default: false ++ 23 | Option to show module when scratchpad is empty. 24 | 25 | *format-icons*: ++ 26 | typeof: array/object ++ 27 | Based on the current scratchpad window counts, the corresponding icon gets selected. 28 | 29 | *tooltip*: ++ 30 | typeof: bool ++ 31 | default: true ++ 32 | Option to disable tooltip on hover. 33 | 34 | *tooltip-format*: ++ 35 | typeof: string ++ 36 | default: {app}: {title} ++ 37 | The format, how information in the tooltip should be displayed. 38 | 39 | # FORMAT REPLACEMENTS 40 | 41 | *{icon}*: Icon, as defined in *format-icons*. 42 | 43 | *{count}*: Number of windows in the scratchpad. 44 | 45 | *{app}*: Name of the application in the scratchpad. 46 | 47 | *{title}*: Title of the application in the scratchpad. 48 | 49 | # EXAMPLES 50 | 51 | ``` 52 | "sway/scratchpad": { 53 | "format": "{icon} {count}", 54 | "show-empty": false, 55 | "format-icons": ["", ""], 56 | "tooltip": true, 57 | "tooltip-format": "{app}: {title}" 58 | } 59 | ``` 60 | 61 | # STYLE 62 | 63 | - *#scratchpad* 64 | - *#scratchpad.empty* 65 | -------------------------------------------------------------------------------- /man/waybar-systemd-failed-units.5.scd: -------------------------------------------------------------------------------- 1 | waybar-systemd-failed-units(5) 2 | 3 | # NAME 4 | 5 | waybar - systemd failed units monitor module 6 | 7 | # DESCRIPTION 8 | 9 | The *systemd-failed-units* module displays the number of failed systemd units. 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *systemd-failed-units* 14 | 15 | *format*: ++ 16 | typeof: string ++ 17 | default: *{nr_failed} failed* ++ 18 | The format, how information should be displayed. This format is used when other formats aren't specified. 19 | 20 | *format-ok*: ++ 21 | typeof: string ++ 22 | This format is used when there is no failing units. 23 | 24 | *user*: ++ 25 | typeof: bool ++ 26 | default: *true* ++ 27 | Option to count user systemd units. 28 | 29 | *system*: ++ 30 | typeof: bool ++ 31 | default: *true* ++ 32 | Option to count systemwide (PID=1) systemd units. 33 | 34 | *hide-on-ok*: ++ 35 | typeof: bool ++ 36 | default: *true* ++ 37 | Option to hide this module when there is no failing units. 38 | 39 | # FORMAT REPLACEMENTS 40 | 41 | *{nr_failed_system}*: Number of failed units from systemwide (PID=1) systemd. 42 | 43 | *{nr_failed_user}*: Number of failed units from user systemd. 44 | 45 | *{nr_failed}*: Number of total failed units. 46 | 47 | # EXAMPLES 48 | 49 | ``` 50 | "systemd-failed-units": { 51 | "hide-on-ok": false, 52 | "format": "✗ {nr_failed}", 53 | "format-ok": "✓", 54 | "system": true, 55 | "user": false, 56 | } 57 | ``` 58 | 59 | # STYLE 60 | 61 | - *#systemd-failed-units* 62 | - *#systemd-failed-units.ok* 63 | - *#systemd-failed-units.degraded* 64 | -------------------------------------------------------------------------------- /src/modules/sway/mode.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/sway/mode.hpp" 2 | 3 | #include 4 | 5 | namespace waybar::modules::sway { 6 | 7 | Mode::Mode(const std::string& id, const Json::Value& config) 8 | : ALabel(config, "mode", id, "{}", 0, true) { 9 | ipc_.subscribe(R"(["mode"])"); 10 | ipc_.signal_event.connect(sigc::mem_fun(*this, &Mode::onEvent)); 11 | // Launch worker 12 | ipc_.setWorker([this] { 13 | try { 14 | ipc_.handleEvent(); 15 | } catch (const std::exception& e) { 16 | spdlog::error("Mode: {}", e.what()); 17 | } 18 | }); 19 | dp.emit(); 20 | } 21 | 22 | void Mode::onEvent(const struct Ipc::ipc_response& res) { 23 | try { 24 | std::lock_guard lock(mutex_); 25 | auto payload = parser_.parse(res.payload); 26 | if (payload["change"] != "default") { 27 | if (payload["pango_markup"].asBool()) { 28 | mode_ = payload["change"].asString(); 29 | } else { 30 | mode_ = Glib::Markup::escape_text(payload["change"].asString()); 31 | } 32 | } else { 33 | mode_.clear(); 34 | } 35 | dp.emit(); 36 | } catch (const std::exception& e) { 37 | spdlog::error("Mode: {}", e.what()); 38 | } 39 | } 40 | 41 | auto Mode::update() -> void { 42 | if (mode_.empty()) { 43 | event_box_.hide(); 44 | } else { 45 | label_.set_markup(fmt::format(fmt::runtime(format_), mode_)); 46 | if (tooltipEnabled()) { 47 | label_.set_tooltip_text(mode_); 48 | } 49 | event_box_.show(); 50 | } 51 | // Call parent update 52 | ALabel::update(); 53 | } 54 | 55 | } // namespace waybar::modules::sway 56 | -------------------------------------------------------------------------------- /include/modules/sway/bar.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "modules/sway/ipc/client.hpp" 6 | #include "util/SafeSignal.hpp" 7 | #include "util/json.hpp" 8 | 9 | namespace waybar { 10 | 11 | class Bar; 12 | 13 | namespace modules::sway { 14 | 15 | /* 16 | * Supported subset of i3/sway IPC barconfig object 17 | */ 18 | struct swaybar_config { 19 | std::string id; 20 | std::string mode; 21 | std::string hidden_state; 22 | }; 23 | 24 | /** 25 | * swaybar IPC client 26 | */ 27 | class BarIpcClient { 28 | public: 29 | BarIpcClient(waybar::Bar& bar); 30 | 31 | private: 32 | void onInitialConfig(const struct Ipc::ipc_response& res); 33 | void onIpcEvent(const struct Ipc::ipc_response&); 34 | void onCmd(const struct Ipc::ipc_response&); 35 | void onConfigUpdate(const swaybar_config& config); 36 | void onVisibilityUpdate(bool visible_by_modifier); 37 | void onModeUpdate(bool visible_by_modifier); 38 | void onUrgencyUpdate(bool visible_by_urgency); 39 | void update(); 40 | bool isModuleEnabled(std::string name); 41 | 42 | Bar& bar_; 43 | util::JsonParser parser_; 44 | Ipc ipc_; 45 | 46 | swaybar_config bar_config_; 47 | std::string modifier_reset_; 48 | bool visible_by_mode_ = false; 49 | bool visible_by_modifier_ = false; 50 | bool visible_by_urgency_ = false; 51 | std::atomic modifier_no_action_ = false; 52 | 53 | SafeSignal signal_mode_; 54 | SafeSignal signal_visible_; 55 | SafeSignal signal_urgency_; 56 | SafeSignal signal_config_; 57 | }; 58 | 59 | } // namespace modules::sway 60 | } // namespace waybar 61 | -------------------------------------------------------------------------------- /man/waybar-river-layout.5.scd: -------------------------------------------------------------------------------- 1 | waybar-river-layout(5) 2 | 3 | # NAME 4 | 5 | waybar - river layout module 6 | 7 | # DESCRIPTION 8 | 9 | The *layout* module displays the current layout in river. 10 | 11 | It may not be set until a layout is first applied. 12 | 13 | # CONFIGURATION 14 | 15 | Addressed by *river/layout* 16 | 17 | *format*: ++ 18 | typeof: string ++ 19 | default: {} ++ 20 | The format, how information should be displayed. On {} data gets inserted. 21 | 22 | *rotate*: ++ 23 | typeof: integer ++ 24 | Positive value to rotate the text label. 25 | 26 | *max-length*: ++ 27 | typeof: integer ++ 28 | The maximum length in character the module should display. 29 | 30 | *min-length*: ++ 31 | typeof: integer ++ 32 | The minimum length in characters the module should accept. 33 | 34 | *align*: ++ 35 | typeof: float ++ 36 | 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. 37 | 38 | *on-click*: ++ 39 | typeof: string ++ 40 | Command to execute when clicked on the module. 41 | 42 | *on-click-middle*: ++ 43 | typeof: string ++ 44 | Command to execute when middle-clicked on the module using mousewheel. 45 | 46 | *on-click-right*: ++ 47 | typeof: string ++ 48 | Command to execute when you right-click on the module. 49 | 50 | # EXAMPLE 51 | 52 | ``` 53 | "river/layout": { 54 | "format": "{}", 55 | "min-length": 4, 56 | "align": "right" 57 | } 58 | ``` 59 | 60 | # STYLE 61 | 62 | - *#layout* 63 | - *#layout.focused* Applied when the output this module's bar belongs to is focused. 64 | 65 | # SEE ALSO 66 | 67 | waybar(5), river(1) 68 | -------------------------------------------------------------------------------- /test/JsonParser.cpp: -------------------------------------------------------------------------------- 1 | #include "util/json.hpp" 2 | 3 | #if __has_include() 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | TEST_CASE("Simple json", "[json]") { 10 | SECTION("Parse simple json") { 11 | std::string stringToTest = R"({"number": 5, "string": "test"})"; 12 | waybar::util::JsonParser parser; 13 | Json::Value jsonValue = parser.parse(stringToTest); 14 | REQUIRE(jsonValue["number"].asInt() == 5); 15 | REQUIRE(jsonValue["string"].asString() == "test"); 16 | } 17 | } 18 | 19 | TEST_CASE("Json with unicode", "[json]") { 20 | SECTION("Parse json with unicode") { 21 | std::string stringToTest = R"({"test": "\xab"})"; 22 | waybar::util::JsonParser parser; 23 | Json::Value jsonValue = parser.parse(stringToTest); 24 | // compare with "\u00ab" because "\xab" is replaced with "\u00ab" in the parser 25 | REQUIRE(jsonValue["test"].asString() == "\u00ab"); 26 | } 27 | } 28 | 29 | TEST_CASE("Json with emoji", "[json]") { 30 | SECTION("Parse json with emoji") { 31 | std::string stringToTest = R"({"test": "😊"})"; 32 | waybar::util::JsonParser parser; 33 | Json::Value jsonValue = parser.parse(stringToTest); 34 | REQUIRE(jsonValue["test"].asString() == "😊"); 35 | } 36 | } 37 | 38 | TEST_CASE("Json with chinese characters", "[json]") { 39 | SECTION("Parse json with chinese characters") { 40 | std::string stringToTest = R"({"test": "你好"})"; 41 | waybar::util::JsonParser parser; 42 | Json::Value jsonValue = parser.parse(stringToTest); 43 | REQUIRE(jsonValue["test"].asString() == "你好"); 44 | } 45 | } -------------------------------------------------------------------------------- /include/modules/cava.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ALabel.hpp" 4 | #include "util/sleeper_thread.hpp" 5 | 6 | namespace cava { 7 | extern "C" { 8 | #include 9 | } 10 | } // namespace cava 11 | 12 | namespace waybar::modules { 13 | using namespace std::literals::chrono_literals; 14 | 15 | class Cava final : public ALabel { 16 | public: 17 | Cava(const std::string&, const Json::Value&); 18 | virtual ~Cava(); 19 | auto update() -> void override; 20 | auto doAction(const std::string& name) -> void override; 21 | 22 | private: 23 | util::SleeperThread thread_; 24 | util::SleeperThread thread_fetch_input_; 25 | 26 | struct cava::error_s error_ {}; // cava errors 27 | struct cava::config_params prm_ {}; // cava parameters 28 | struct cava::audio_raw audio_raw_ {}; // cava handled raw audio data(is based on audio_data) 29 | struct cava::audio_data audio_data_ {}; // cava audio data 30 | struct cava::cava_plan* plan_; //{new cava_plan{}}; 31 | // Cava API to read audio source 32 | cava::ptr input_source_; 33 | // Delay to handle audio source 34 | std::chrono::milliseconds frame_time_milsec_{1s}; 35 | // Text to display 36 | std::string text_{""}; 37 | int rePaint_{1}; 38 | std::chrono::seconds fetch_input_delay_{4}; 39 | std::chrono::seconds suspend_silence_delay_{0}; 40 | bool silence_{false}; 41 | bool hide_on_silence_{false}; 42 | int sleep_counter_{0}; 43 | // Cava method 44 | void pause_resume(); 45 | // ModuleActionMap 46 | static inline std::map actionMap_{ 47 | {"mode", &waybar::modules::Cava::pause_resume}}; 48 | }; 49 | } // namespace waybar::modules 50 | -------------------------------------------------------------------------------- /src/util/enum.cpp: -------------------------------------------------------------------------------- 1 | #include "util/enum.hpp" 2 | 3 | #include // for std::transform 4 | #include // for std::toupper 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "modules/hyprland/workspaces.hpp" 11 | #include "util/string.hpp" 12 | 13 | namespace waybar::util { 14 | 15 | template 16 | EnumParser::EnumParser() = default; 17 | 18 | template 19 | EnumParser::~EnumParser() = default; 20 | 21 | template 22 | EnumType EnumParser::parseStringToEnum(const std::string& str, 23 | const std::map& enumMap) { 24 | // Convert the input string to uppercase 25 | std::string uppercaseStr = capitalize(str); 26 | 27 | // Capitalize the map keys before searching 28 | std::map capitalizedEnumMap; 29 | std::transform( 30 | enumMap.begin(), enumMap.end(), std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()), 31 | [](const auto& pair) { return std::make_pair(capitalize(pair.first), pair.second); }); 32 | 33 | // Return enum match of string 34 | auto it = capitalizedEnumMap.find(uppercaseStr); 35 | if (it != capitalizedEnumMap.end()) return it->second; 36 | 37 | // Throw error if it doesn't return 38 | throw std::invalid_argument("Invalid string representation for enum"); 39 | } 40 | 41 | // Explicit instantiations for specific EnumType types you intend to use 42 | // Add explicit instantiations for all relevant EnumType types 43 | template struct EnumParser; 44 | 45 | } // namespace waybar::util 46 | -------------------------------------------------------------------------------- /resources/icons/waybar-privacy-screen-share-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/util/prepare_for_sleep.cpp: -------------------------------------------------------------------------------- 1 | #include "util/prepare_for_sleep.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace { 7 | class PrepareForSleep { 8 | private: 9 | PrepareForSleep() { 10 | login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); 11 | if (!login1_connection) { 12 | spdlog::warn("Unable to connect to the SYSTEM Bus!..."); 13 | } else { 14 | login1_id = g_dbus_connection_signal_subscribe( 15 | login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager", 16 | "PrepareForSleep", "/org/freedesktop/login1", NULL, G_DBUS_SIGNAL_FLAGS_NONE, 17 | prepareForSleep_cb, this, NULL); 18 | } 19 | } 20 | 21 | static void prepareForSleep_cb(GDBusConnection *system_bus, const gchar *sender_name, 22 | const gchar *object_path, const gchar *interface_name, 23 | const gchar *signal_name, GVariant *parameters, 24 | gpointer user_data) { 25 | if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)"))) { 26 | gboolean sleeping; 27 | g_variant_get(parameters, "(b)", &sleeping); 28 | 29 | PrepareForSleep *self = static_cast(user_data); 30 | self->signal.emit(sleeping); 31 | } 32 | } 33 | 34 | public: 35 | static PrepareForSleep &GetInstance() { 36 | static PrepareForSleep instance; 37 | return instance; 38 | } 39 | waybar::SafeSignal signal; 40 | 41 | private: 42 | guint login1_id; 43 | GDBusConnection *login1_connection; 44 | }; 45 | } // namespace 46 | 47 | waybar::SafeSignal &waybar::util::prepare_for_sleep() { 48 | return PrepareForSleep::GetInstance().signal; 49 | } 50 | -------------------------------------------------------------------------------- /src/modules/cpu_frequency/linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "modules/cpu_frequency.hpp" 4 | 5 | std::vector waybar::modules::CpuFrequency::parseCpuFrequencies() { 6 | const std::string file_path_ = "/proc/cpuinfo"; 7 | std::ifstream info(file_path_); 8 | if (!info.is_open()) { 9 | throw std::runtime_error("Can't open " + file_path_); 10 | } 11 | std::vector frequencies; 12 | std::string line; 13 | while (getline(info, line)) { 14 | if (line.substr(0, 7).compare("cpu MHz") != 0) { 15 | continue; 16 | } 17 | 18 | std::string frequency_str = line.substr(line.find(":") + 2); 19 | float frequency = std::strtol(frequency_str.c_str(), nullptr, 10); 20 | frequencies.push_back(frequency); 21 | } 22 | info.close(); 23 | 24 | if (frequencies.size() <= 0) { 25 | std::string cpufreq_dir = "/sys/devices/system/cpu/cpufreq"; 26 | if (std::filesystem::exists(cpufreq_dir)) { 27 | std::vector frequency_files = {"/cpuinfo_min_freq", "/cpuinfo_max_freq"}; 28 | for (auto& p : std::filesystem::directory_iterator(cpufreq_dir)) { 29 | for (auto freq_file : frequency_files) { 30 | std::string freq_file_path = p.path().string() + freq_file; 31 | if (std::filesystem::exists(freq_file_path)) { 32 | std::string freq_value; 33 | std::ifstream freq(freq_file_path); 34 | if (freq.is_open()) { 35 | getline(freq, freq_value); 36 | float frequency = std::strtol(freq_value.c_str(), nullptr, 10); 37 | frequencies.push_back(frequency / 1000); 38 | freq.close(); 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | return frequencies; 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/clang-tidy.yml: -------------------------------------------------------------------------------- 1 | name: clang-tidy 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-tidy-${{ github.event.pull_request.number || github.ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | container: 13 | image: alexays/waybar:debian 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: configure 17 | run: | 18 | meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json 19 | ninja -C build # necessary to find certain .h files (xdg, wayland, etc.) 20 | - uses: actions/setup-python@v5 21 | with: 22 | python-version: '3.10' # to be kept in sync with cpp-linter-action 23 | update-environment: true # the python dist installed by the action needs LD_LIBRARY_PATH to work 24 | - uses: cpp-linter/cpp-linter-action@v2.9.1 25 | name: clang-tidy 26 | id: clang-tidy-check 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | PIP_NO_CACHE_DIR: false 30 | with: 31 | style: "" # empty string => don't do clang-format checks here, we do them in clang-format.yml 32 | files-changed-only: true # only check files that have changed 33 | lines-changed-only: true # only check lines that have changed 34 | tidy-checks: "" # empty string => use the .clang-tidy file 35 | version: "17" # clang-tools version 36 | database: "build" # path to the compile_commands.json file 37 | - name: Check if clang-tidy failed on any files 38 | if: steps.clang-tidy-check.outputs.checks-failed > 0 39 | run: echo "Some files failed the linting checks!" && exit 1 40 | -------------------------------------------------------------------------------- /include/modules/hyprland/window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "AAppIconLabel.hpp" 8 | #include "bar.hpp" 9 | #include "modules/hyprland/backend.hpp" 10 | #include "util/json.hpp" 11 | 12 | namespace waybar::modules::hyprland { 13 | 14 | class Window : public waybar::AAppIconLabel, public EventHandler { 15 | public: 16 | Window(const std::string&, const waybar::Bar&, const Json::Value&); 17 | ~Window() override; 18 | 19 | auto update() -> void override; 20 | 21 | private: 22 | struct Workspace { 23 | int id; 24 | int windows; 25 | std::string last_window; 26 | std::string last_window_title; 27 | 28 | static auto parse(const Json::Value& value) -> Workspace; 29 | }; 30 | 31 | struct WindowData { 32 | bool floating; 33 | int monitor = -1; 34 | std::string class_name; 35 | std::string initial_class_name; 36 | std::string title; 37 | std::string initial_title; 38 | bool fullscreen; 39 | bool grouped; 40 | 41 | static auto parse(const Json::Value&) -> WindowData; 42 | }; 43 | 44 | static auto getActiveWorkspace(const std::string&) -> Workspace; 45 | static auto getActiveWorkspace() -> Workspace; 46 | void onEvent(const std::string& ev) override; 47 | void queryActiveWorkspace(); 48 | void setClass(const std::string&, bool enable); 49 | 50 | bool separateOutputs_; 51 | std::mutex mutex_; 52 | const Bar& bar_; 53 | util::JsonParser parser_; 54 | WindowData windowData_; 55 | Workspace workspace_; 56 | std::string soloClass_; 57 | std::string lastSoloClass_; 58 | bool solo_; 59 | bool allFloating_; 60 | bool swallowing_; 61 | bool fullscreen_; 62 | bool focused_; 63 | }; 64 | 65 | } // namespace waybar::modules::hyprland 66 | -------------------------------------------------------------------------------- /include/util/regex_collection.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace waybar::util { 10 | 11 | struct Rule { 12 | std::regex rule; 13 | std::string repr; 14 | int priority; 15 | 16 | // Fix for Clang < 16 17 | // See https://en.cppreference.com/w/cpp/compiler_support/20 "Parenthesized initialization of 18 | // aggregates" 19 | Rule(std::regex rule, std::string repr, int priority) 20 | : rule(rule), repr(repr), priority(priority) {} 21 | }; 22 | 23 | int default_priority_function(std::string& key); 24 | 25 | /* A collection of regexes and strings, with a default string to return if no regexes. 26 | * When a regex is matched, the corresponding string is returned. 27 | * All regexes that are matched are cached, so that the regexes are only 28 | * evaluated once against a given string. 29 | * Regexes may be given a higher priority than others, so that they are matched 30 | * first. The priority function is given the regex string, and should return a 31 | * higher number for higher priority regexes. 32 | */ 33 | class RegexCollection { 34 | private: 35 | std::vector rules; 36 | std::map regex_cache; 37 | std::string default_repr; 38 | 39 | std::string& find_match(std::string& value, bool& matched_any); 40 | 41 | public: 42 | RegexCollection() = default; 43 | RegexCollection(const Json::Value& map, std::string default_repr = "", 44 | std::function priority_function = default_priority_function); 45 | ~RegexCollection() = default; 46 | 47 | std::string& get(std::string& value, bool& matched_any); 48 | std::string& get(std::string& value); 49 | }; 50 | 51 | } // namespace waybar::util -------------------------------------------------------------------------------- /include/modules/sni/watcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace waybar::modules::SNI { 8 | 9 | class Watcher { 10 | private: 11 | Watcher(); 12 | 13 | public: 14 | ~Watcher(); 15 | 16 | using singleton = std::shared_ptr; 17 | static singleton getInstance() { 18 | static std::weak_ptr weak; 19 | 20 | std::shared_ptr strong = weak.lock(); 21 | if (!strong) { 22 | strong = std::shared_ptr(new Watcher()); 23 | weak = strong; 24 | } 25 | return strong; 26 | } 27 | 28 | private: 29 | typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType; 30 | 31 | typedef struct { 32 | GfWatchType type; 33 | Watcher *watcher; 34 | gchar *service; 35 | gchar *bus_name; 36 | gchar *object_path; 37 | guint watch_id; 38 | } GfWatch; 39 | 40 | void busAcquired(const Glib::RefPtr &, Glib::ustring); 41 | static gboolean handleRegisterHost(Watcher *, GDBusMethodInvocation *, const gchar *); 42 | static gboolean handleRegisterItem(Watcher *, GDBusMethodInvocation *, const gchar *); 43 | static GfWatch *gfWatchFind(GSList *list, const gchar *bus_name, const gchar *object_path); 44 | static GfWatch *gfWatchNew(GfWatchType, const gchar *, const gchar *, const gchar *, Watcher *); 45 | static void nameVanished(GDBusConnection *connection, const char *name, gpointer data); 46 | static void gfWatchFree(gpointer data); 47 | 48 | void updateRegisteredItems(SnWatcher *obj); 49 | 50 | uint32_t bus_name_id_; 51 | GSList *hosts_ = nullptr; 52 | GSList *items_ = nullptr; 53 | SnWatcher *watcher_ = nullptr; 54 | }; 55 | 56 | } // namespace waybar::modules::SNI 57 | -------------------------------------------------------------------------------- /include/modules/sni/host.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "bar.hpp" 11 | #include "modules/sni/item.hpp" 12 | 13 | namespace waybar::modules::SNI { 14 | 15 | class Host { 16 | public: 17 | Host(const std::size_t id, const Json::Value&, const Bar&, 18 | const std::function&)>&, 19 | const std::function&)>&); 20 | ~Host(); 21 | 22 | private: 23 | void busAcquired(const Glib::RefPtr&, Glib::ustring); 24 | void nameAppeared(const Glib::RefPtr&, Glib::ustring, 25 | const Glib::ustring&); 26 | void nameVanished(const Glib::RefPtr&, Glib::ustring); 27 | static void proxyReady(GObject*, GAsyncResult*, gpointer); 28 | static void registerHost(GObject*, GAsyncResult*, gpointer); 29 | static void itemRegistered(SnWatcher*, const gchar*, gpointer); 30 | static void itemUnregistered(SnWatcher*, const gchar*, gpointer); 31 | 32 | std::tuple getBusNameAndObjectPath(const std::string); 33 | void addRegisteredItem(std::string service); 34 | 35 | std::vector> items_; 36 | const std::string bus_name_; 37 | const std::string object_path_; 38 | std::size_t bus_name_id_; 39 | std::size_t watcher_id_; 40 | GCancellable* cancellable_ = nullptr; 41 | SnWatcher* watcher_ = nullptr; 42 | const Json::Value& config_; 43 | const Bar& bar_; 44 | const std::function&)> on_add_; 45 | const std::function&)> on_remove_; 46 | }; 47 | 48 | } // namespace waybar::modules::SNI 49 | -------------------------------------------------------------------------------- /include/modules/cffi.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "AModule.hpp" 6 | #include "util/command.hpp" 7 | #include "util/json.hpp" 8 | #include "util/sleeper_thread.hpp" 9 | 10 | namespace waybar::modules { 11 | 12 | namespace ffi { 13 | extern "C" { 14 | typedef struct wbcffi_module wbcffi_module; 15 | 16 | typedef struct { 17 | wbcffi_module* obj; 18 | const char* waybar_version; 19 | GtkContainer* (*get_root_widget)(wbcffi_module*); 20 | void (*queue_update)(wbcffi_module*); 21 | } wbcffi_init_info; 22 | 23 | struct wbcffi_config_entry { 24 | const char* key; 25 | const char* value; 26 | }; 27 | } 28 | } // namespace ffi 29 | 30 | class CFFI : public AModule { 31 | public: 32 | CFFI(const std::string&, const std::string&, const Json::Value&); 33 | virtual ~CFFI(); 34 | 35 | virtual auto refresh(int signal) -> void override; 36 | virtual auto doAction(const std::string& name) -> void override; 37 | virtual auto update() -> void override; 38 | 39 | private: 40 | /// 41 | void* cffi_instance_ = nullptr; 42 | 43 | typedef void*(InitFn)(const ffi::wbcffi_init_info* init_info, 44 | const ffi::wbcffi_config_entry* config_entries, size_t config_entries_len); 45 | typedef void(DenitFn)(void* instance); 46 | typedef void(RefreshFn)(void* instance, int signal); 47 | typedef void(DoActionFn)(void* instance, const char* name); 48 | typedef void(UpdateFn)(void* instance); 49 | 50 | // FFI hooks 51 | struct { 52 | std::function init = nullptr; 53 | std::function deinit = nullptr; 54 | std::function refresh = [](void*, int) {}; 55 | std::function doAction = [](void*, const char*) {}; 56 | std::function update = [](void*) {}; 57 | } hooks_; 58 | }; 59 | 60 | } // namespace waybar::modules 61 | -------------------------------------------------------------------------------- /man/waybar-river-mode.5.scd: -------------------------------------------------------------------------------- 1 | waybar-river-mode(5) 2 | 3 | # NAME 4 | 5 | waybar - river mode module 6 | 7 | # DESCRIPTION 8 | 9 | The *mode* module displays the current mapping mode of river. 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *river/mode* 14 | 15 | *format*: ++ 16 | typeof: string ++ 17 | default: {} ++ 18 | The format, how information should be displayed. On {} data gets inserted. 19 | 20 | *rotate*: ++ 21 | typeof: integer ++ 22 | Positive value to rotate the text label. 23 | 24 | *max-length*: ++ 25 | typeof: integer ++ 26 | The maximum length in character the module should display. 27 | 28 | *min-length*: ++ 29 | typeof: integer ++ 30 | The minimum length in characters the module should accept. 31 | 32 | *align*: ++ 33 | typeof: float ++ 34 | 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. 35 | 36 | *on-click*: ++ 37 | typeof: string ++ 38 | Command to execute when clicked on the module. 39 | 40 | *on-click-middle*: ++ 41 | typeof: string ++ 42 | Command to execute when middle-clicked on the module using mousewheel. 43 | 44 | *on-click-right*: ++ 45 | typeof: string ++ 46 | Command to execute when you right-click on the module. 47 | 48 | *on-update*: ++ 49 | typeof: string ++ 50 | Command to execute when the module is updated. 51 | 52 | *on-scroll-up*: ++ 53 | typeof: string ++ 54 | Command to execute when scrolling up on the module. 55 | 56 | *on-scroll-down*: ++ 57 | typeof: string ++ 58 | Command to execute when scrolling down on the module. 59 | 60 | *smooth-scrolling-threshold*: ++ 61 | typeof: double ++ 62 | Threshold to be used when scrolling. 63 | 64 | # EXAMPLES 65 | 66 | ``` 67 | "river/mode": { 68 | "format": " {}" 69 | } 70 | ``` 71 | 72 | # STYLE 73 | 74 | - *#mode* 75 | - *#mode.* 76 | -------------------------------------------------------------------------------- /src/modules/hyprland/submap.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/hyprland/submap.hpp" 2 | 3 | #include 4 | 5 | #include "util/sanitize_str.hpp" 6 | 7 | namespace waybar::modules::hyprland { 8 | 9 | Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) 10 | : ALabel(config, "submap", id, "{}", 0, true), bar_(bar) { 11 | modulesReady = true; 12 | 13 | if (!gIPC) { 14 | gIPC = std::make_unique(); 15 | } 16 | 17 | label_.hide(); 18 | ALabel::update(); 19 | 20 | // register for hyprland ipc 21 | gIPC->registerForIPC("submap", this); 22 | dp.emit(); 23 | } 24 | 25 | Submap::~Submap() { 26 | gIPC->unregisterForIPC(this); 27 | // wait for possible event handler to finish 28 | std::lock_guard lg(mutex_); 29 | } 30 | 31 | auto Submap::update() -> void { 32 | std::lock_guard lg(mutex_); 33 | 34 | if (submap_.empty()) { 35 | event_box_.hide(); 36 | } else { 37 | label_.set_markup(fmt::format(fmt::runtime(format_), submap_)); 38 | if (tooltipEnabled()) { 39 | label_.set_tooltip_text(submap_); 40 | } 41 | event_box_.show(); 42 | } 43 | // Call parent update 44 | ALabel::update(); 45 | } 46 | 47 | void Submap::onEvent(const std::string& ev) { 48 | std::lock_guard lg(mutex_); 49 | 50 | if (ev.find("submap") == std::string::npos) { 51 | return; 52 | } 53 | 54 | auto submapName = ev.substr(ev.find_last_of('>') + 1); 55 | submapName = waybar::util::sanitize_string(submapName); 56 | 57 | if (!submap_.empty()) { 58 | label_.get_style_context()->remove_class(submap_); 59 | } 60 | 61 | submap_ = submapName; 62 | 63 | label_.get_style_context()->add_class(submap_); 64 | 65 | spdlog::debug("hyprland submap onevent with {}", submap_); 66 | 67 | dp.emit(); 68 | } 69 | } // namespace waybar::modules::hyprland 70 | -------------------------------------------------------------------------------- /man/waybar-privacy.5.scd: -------------------------------------------------------------------------------- 1 | waybar-privacy(5) 2 | 3 | # NAME 4 | 5 | waybar - privacy module 6 | 7 | # DESCRIPTION 8 | 9 | The *privacy* module displays if any application is capturing audio, sharing ++ 10 | the screen or playing audio. 11 | 12 | # CONFIGURATION 13 | 14 | *icon-spacing*: ++ 15 | typeof: integer ++ 16 | default: 4 ++ 17 | The spacing between each privacy icon. 18 | 19 | *icon-size*: ++ 20 | typeof: integer ++ 21 | default: 20 ++ 22 | The size of each privacy icon. 23 | 24 | *transition-duration*: ++ 25 | typeof: integer ++ 26 | default: 250 ++ 27 | Option to disable tooltip on hover. 28 | 29 | *modules* ++ 30 | typeof: array of objects ++ 31 | default: [{"type": "screenshare"}, {"type": "audio-in"}] ++ 32 | Which privacy modules to monitor. See *MODULES CONFIGURATION* for++ 33 | more information. 34 | 35 | # MODULES CONFIGURATION 36 | 37 | *type*: ++ 38 | typeof: string ++ 39 | values: "screenshare", "audio-in", "audio-out" ++ 40 | Specifies which module to use and configure. 41 | 42 | *tooltip*: ++ 43 | typeof: bool ++ 44 | default: true ++ 45 | Option to disable tooltip on hover. 46 | 47 | *tooltip-icon-size*: ++ 48 | typeof: integer ++ 49 | default: 24 ++ 50 | The size of each icon in the tooltip. 51 | 52 | # EXAMPLES 53 | 54 | ``` 55 | "privacy": { 56 | "icon-spacing": 4, 57 | "icon-size": 18, 58 | "transition-duration": 250, 59 | "modules": [ 60 | { 61 | "type": "screenshare", 62 | "tooltip": true, 63 | "tooltip-icon-size": 24 64 | }, 65 | { 66 | "type": "audio-out", 67 | "tooltip": true, 68 | "tooltip-icon-size": 24 69 | }, 70 | { 71 | "type": "audio-in", 72 | "tooltip": true, 73 | "tooltip-icon-size": 24 74 | } 75 | ] 76 | }, 77 | ``` 78 | 79 | # STYLE 80 | 81 | - *#privacy* 82 | - *#privacy-item* 83 | - *#privacy-item.screenshare* 84 | - *#privacy-item.audio-in* 85 | - *#privacy-item.audio-out* 86 | -------------------------------------------------------------------------------- /include/util/pipewire/privacy_node_info.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "util/gtk_icon.hpp" 8 | 9 | namespace waybar::util::PipewireBackend { 10 | 11 | enum PrivacyNodeType { 12 | PRIVACY_NODE_TYPE_NONE, 13 | PRIVACY_NODE_TYPE_VIDEO_INPUT, 14 | PRIVACY_NODE_TYPE_AUDIO_INPUT, 15 | PRIVACY_NODE_TYPE_AUDIO_OUTPUT 16 | }; 17 | 18 | class PrivacyNodeInfo { 19 | public: 20 | PrivacyNodeType type = PRIVACY_NODE_TYPE_NONE; 21 | uint32_t id; 22 | uint32_t client_id; 23 | enum pw_node_state state = PW_NODE_STATE_IDLE; 24 | std::string media_class; 25 | std::string media_name; 26 | std::string node_name; 27 | std::string application_name; 28 | 29 | std::string pipewire_access_portal_app_id; 30 | std::string application_icon_name; 31 | 32 | struct spa_hook object_listener; 33 | struct spa_hook proxy_listener; 34 | 35 | void *data; 36 | 37 | std::string get_name() { 38 | const std::vector names{&application_name, &node_name}; 39 | std::string name = "Unknown Application"; 40 | for (auto &name_ : names) { 41 | if (name_ != nullptr && name_->length() > 0) { 42 | name = *name_; 43 | name[0] = toupper(name[0]); 44 | break; 45 | } 46 | } 47 | return name; 48 | } 49 | 50 | std::string get_icon_name() { 51 | const std::vector names{&application_icon_name, &pipewire_access_portal_app_id, 52 | &application_name, &node_name}; 53 | const std::string name = "application-x-executable-symbolic"; 54 | for (auto &name_ : names) { 55 | if (name_ != nullptr && name_->length() > 0 && DefaultGtkIconThemeWrapper::has_icon(*name_)) { 56 | return *name_; 57 | } 58 | } 59 | return name; 60 | } 61 | }; 62 | } // namespace waybar::util::PipewireBackend 63 | -------------------------------------------------------------------------------- /man/waybar-sway-mode.5.scd: -------------------------------------------------------------------------------- 1 | waybar-sway-mode(5) 2 | 3 | # NAME 4 | 5 | waybar - sway mode module 6 | 7 | # DESCRIPTION 8 | 9 | The *mode* module displays the current binding mode of Sway 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *sway/mode* 14 | 15 | *format*: ++ 16 | typeof: string ++ 17 | default: {} ++ 18 | The format, how information should be displayed. On {} data gets inserted. 19 | 20 | *rotate*: ++ 21 | typeof: integer ++ 22 | Positive value to rotate the text label. 23 | 24 | *max-length*: ++ 25 | typeof: integer ++ 26 | The maximum length in character the module should display. 27 | 28 | *min-length*: ++ 29 | typeof: integer ++ 30 | The minimum length in characters the module should accept. 31 | 32 | *align*: ++ 33 | typeof: float ++ 34 | 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. 35 | 36 | *on-click*: ++ 37 | typeof: string ++ 38 | Command to execute when clicked on the module. 39 | 40 | *on-click-middle*: ++ 41 | typeof: string ++ 42 | Command to execute when middle-clicked on the module using mousewheel. 43 | 44 | *on-click-right*: ++ 45 | typeof: string ++ 46 | Command to execute when you right-click on the module. 47 | 48 | *on-update*: ++ 49 | typeof: string ++ 50 | Command to execute when the module is updated. 51 | 52 | *on-scroll-up*: ++ 53 | typeof: string ++ 54 | Command to execute when scrolling up on the module. 55 | 56 | *on-scroll-down*: ++ 57 | typeof: string ++ 58 | Command to execute when scrolling down on the module. 59 | 60 | *smooth-scrolling-threshold*: ++ 61 | typeof: double ++ 62 | Threshold to be used when scrolling. 63 | 64 | *tooltip*: ++ 65 | typeof: bool ++ 66 | default: true ++ 67 | Option to disable tooltip on hover. 68 | 69 | # EXAMPLES 70 | 71 | ``` 72 | "sway/mode": { 73 | "format": " {}", 74 | "max-length": 50 75 | } 76 | ``` 77 | 78 | # STYLE 79 | 80 | - *#mode* 81 | -------------------------------------------------------------------------------- /include/modules/mpd/mpd.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "ALabel.hpp" 11 | #include "modules/mpd/state.hpp" 12 | 13 | namespace waybar::modules { 14 | 15 | class MPD : public ALabel { 16 | friend class detail::Context; 17 | 18 | // State machine 19 | detail::Context context_{this}; 20 | 21 | const std::string module_name_; 22 | 23 | // Not using unique_ptr since we don't manage the pointer 24 | // (It's either nullptr, or from the config) 25 | const char* server_; 26 | const unsigned port_; 27 | const std::string password_; 28 | 29 | unsigned timeout_; 30 | 31 | detail::unique_connection connection_; 32 | 33 | detail::unique_status status_; 34 | mpd_state state_; 35 | detail::unique_song song_; 36 | 37 | public: 38 | MPD(const std::string&, const Json::Value&); 39 | virtual ~MPD() noexcept = default; 40 | auto update() -> void override; 41 | 42 | private: 43 | std::string getTag(mpd_tag_type type, unsigned idx = 0) const; 44 | std::string getFilename() const; 45 | void setLabel(); 46 | std::string getStateIcon() const; 47 | std::string getOptionIcon(std::string optionName, bool activated) const; 48 | 49 | // GUI-side methods 50 | bool handlePlayPause(GdkEventButton* const&); 51 | void emit() { dp.emit(); } 52 | 53 | // MPD-side, Non-GUI methods. 54 | void tryConnect(); 55 | void checkErrors(mpd_connection* conn); 56 | void fetchState(); 57 | void queryMPD(); 58 | 59 | inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; } 60 | inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; } 61 | inline bool paused() const { return connection_ && state_ == MPD_STATE_PAUSE; } 62 | }; 63 | 64 | #if !defined(MPD_NOINLINE) 65 | #include "modules/mpd/state.inl.hpp" 66 | #endif 67 | 68 | } // namespace waybar::modules 69 | -------------------------------------------------------------------------------- /include/modules/sway/language.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "ALabel.hpp" 10 | #include "bar.hpp" 11 | #include "client.hpp" 12 | #include "modules/sway/ipc/client.hpp" 13 | #include "util/json.hpp" 14 | 15 | namespace waybar::modules::sway { 16 | 17 | class Language : public ALabel, public sigc::trackable { 18 | public: 19 | Language(const std::string& id, const Json::Value& config); 20 | virtual ~Language() = default; 21 | auto update() -> void override; 22 | 23 | private: 24 | enum class DispayedShortFlag { None = 0, ShortName = 1, ShortDescription = 1 << 1 }; 25 | 26 | struct Layout { 27 | std::string full_name; 28 | std::string short_name; 29 | std::string variant; 30 | std::string short_description; 31 | std::string country_flag() const; 32 | }; 33 | 34 | class XKBContext { 35 | public: 36 | XKBContext(); 37 | ~XKBContext(); 38 | auto next_layout() -> Layout*; 39 | 40 | private: 41 | rxkb_context* context_ = nullptr; 42 | rxkb_layout* xkb_layout_ = nullptr; 43 | Layout* layout_ = nullptr; 44 | std::map base_layouts_by_name_; 45 | }; 46 | 47 | void onEvent(const struct Ipc::ipc_response&); 48 | void onCmd(const struct Ipc::ipc_response&); 49 | 50 | auto set_current_layout(std::string current_layout) -> void; 51 | auto init_layouts_map(const std::vector& used_layouts) -> void; 52 | 53 | const static std::string XKB_LAYOUT_NAMES_KEY; 54 | const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY; 55 | 56 | Layout layout_; 57 | std::string tooltip_format_ = ""; 58 | std::map layouts_map_; 59 | bool hide_single_; 60 | bool is_variant_displayed; 61 | std::byte displayed_short_flag = static_cast(DispayedShortFlag::None); 62 | 63 | util::JsonParser parser_; 64 | std::mutex mutex_; 65 | Ipc ipc_; 66 | }; 67 | 68 | } // namespace waybar::modules::sway 69 | -------------------------------------------------------------------------------- /man/waybar-hyprland-submap.5.scd: -------------------------------------------------------------------------------- 1 | waybar-hyprland-submap(5) 2 | 3 | # NAME 4 | 5 | waybar - hyprland submap module 6 | 7 | # DESCRIPTION 8 | 9 | The *submap* module displays the currently active submap similar to *sway/mode*. 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *hyprland/submap* 14 | 15 | *format*: ++ 16 | typeof: string ++ 17 | default: {} ++ 18 | The format, how information should be displayed. On {} the currently active submap is displayed. 19 | 20 | *rotate*: ++ 21 | typeof: integer ++ 22 | Positive value to rotate the text label. 23 | 24 | *max-length*: ++ 25 | typeof: integer ++ 26 | The maximum length in character the module should display. 27 | 28 | *min-length*: ++ 29 | typeof: integer ++ 30 | The minimum length in characters the module should accept. 31 | 32 | *align*: ++ 33 | typeof: float ++ 34 | 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. 35 | 36 | *on-click*: ++ 37 | typeof: string ++ 38 | Command to execute when clicked on the module. 39 | 40 | *on-click-middle*: ++ 41 | typeof: string ++ 42 | Command to execute when middle-clicked on the module using mousewheel. 43 | 44 | *on-click-right*: ++ 45 | typeof: string ++ 46 | Command to execute when you right-click on the module. 47 | 48 | *on-update*: ++ 49 | typeof: string ++ 50 | Command to execute when the module is updated. 51 | 52 | *on-scroll-up*: ++ 53 | typeof: string ++ 54 | Command to execute when scrolling up on the module. 55 | 56 | *on-scroll-down*: ++ 57 | typeof: string ++ 58 | Command to execute when scrolling down on the module. 59 | 60 | *smooth-scrolling-threshold*: ++ 61 | typeof: double ++ 62 | Threshold to be used when scrolling. 63 | 64 | *tooltip*: ++ 65 | typeof: bool ++ 66 | default: true ++ 67 | Option to disable tooltip on hover. 68 | 69 | 70 | # EXAMPLES 71 | 72 | ``` 73 | "hyprland/submap": { 74 | "format": "✌️ {}", 75 | "max-length": 8, 76 | "tooltip": false 77 | } 78 | ``` 79 | 80 | # STYLE 81 | 82 | - *#submap* 83 | - *#submap.* 84 | -------------------------------------------------------------------------------- /protocol/dbus-status-notifier-watcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /man/waybar-pulseaudio-slider.5.scd: -------------------------------------------------------------------------------- 1 | waybar-pulseaudio-slider(5) 2 | 3 | # NAME 4 | 5 | waybar - pulseaudio slider module 6 | 7 | # DESCRIPTION 8 | 9 | The *pulseaudio slider* module displays and controls the current volume of the default sink or source as a bar. 10 | 11 | The volume can be controlled by dragging the slider across the bar or clicking on a specific position. 12 | 13 | # CONFIGURATION 14 | 15 | *min*: ++ 16 | typeof: int ++ 17 | default: 0 ++ 18 | The minimum volume value the slider should display and set. 19 | 20 | *max*: ++ 21 | typeof: int ++ 22 | default: 100 ++ 23 | The maximum volume value the slider should display and set. 24 | 25 | *orientation*: ++ 26 | typeof: string ++ 27 | default: horizontal ++ 28 | The orientation of the slider. Can be either `horizontal` or `vertical`. 29 | 30 | # EXAMPLES 31 | 32 | ``` 33 | "modules-right": [ 34 | "pulseaudio-slider", 35 | ], 36 | "pulseaudio/slider": { 37 | "min": 0, 38 | "max": 100, 39 | "orientation": "horizontal" 40 | } 41 | ``` 42 | 43 | # STYLE 44 | 45 | The slider is a component with multiple CSS Nodes, of which the following are exposed: 46 | 47 | *#pulseaudio-slider*: ++ 48 | Controls the style of the box *around* the slider and bar. 49 | 50 | *#pulseaudio-slider slider*: ++ 51 | Controls the style of the slider handle. 52 | 53 | *#pulseaudio-slider trough*: ++ 54 | Controls the style of the part of the bar that has not been filled. 55 | 56 | *#pulseaudio-slider highlight*: ++ 57 | Controls the style of the part of the bar that has been filled. 58 | 59 | ## STYLE EXAMPLE 60 | 61 | ``` 62 | #pulseaudio-slider slider { 63 | min-height: 0px; 64 | min-width: 0px; 65 | opacity: 0; 66 | background-image: none; 67 | border: none; 68 | box-shadow: none; 69 | } 70 | 71 | #pulseaudio-slider trough { 72 | min-height: 80px; 73 | min-width: 10px; 74 | border-radius: 5px; 75 | background-color: black; 76 | } 77 | 78 | #pulseaudio-slider highlight { 79 | min-width: 10px; 80 | border-radius: 5px; 81 | background-color: green; 82 | } 83 | ``` 84 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.') 2 | option('libinput', type: 'feature', value: 'auto', description: 'Enable libinput support for libinput related features') 3 | option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features') 4 | option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features') 5 | option('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features') 6 | option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio') 7 | option('upower_glib', type: 'feature', value: 'auto', description: 'Enable support for upower') 8 | option('pipewire', type: 'feature', value: 'auto', description: 'Enable support for pipewire') 9 | option('mpris', type: 'feature', value: 'auto', description: 'Enable support for mpris') 10 | option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit') 11 | option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray') 12 | option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') 13 | option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon') 14 | option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL') 15 | option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio') 16 | option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind') 17 | option('tests', type: 'feature', value: 'auto', description: 'Enable tests') 18 | option('experimental', type : 'boolean', value : false, description: 'Enable experimental features') 19 | option('jack', type: 'feature', value: 'auto', description: 'Enable support for JACK') 20 | option('wireplumber', type: 'feature', value: 'auto', description: 'Enable support for WirePlumber') 21 | option('cava', type: 'feature', value: 'auto', description: 'Enable support for Cava') 22 | -------------------------------------------------------------------------------- /src/util/regex_collection.cpp: -------------------------------------------------------------------------------- 1 | #include "util/regex_collection.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace waybar::util { 7 | 8 | int default_priority_function(std::string& key) { return 0; } 9 | 10 | RegexCollection::RegexCollection(const Json::Value& map, std::string default_repr, 11 | std::function priority_function) 12 | : default_repr(default_repr) { 13 | if (!map.isObject()) { 14 | spdlog::warn("Mapping is not an object"); 15 | return; 16 | } 17 | 18 | for (auto it = map.begin(); it != map.end(); ++it) { 19 | if (it.key().isString() && it->isString()) { 20 | std::string key = it.key().asString(); 21 | int priority = priority_function(key); 22 | try { 23 | const std::regex rule{key, std::regex_constants::icase}; 24 | rules.emplace_back(rule, it->asString(), priority); 25 | } catch (const std::regex_error& e) { 26 | spdlog::error("Invalid rule '{}': {}", key, e.what()); 27 | } 28 | } 29 | } 30 | 31 | std::sort(rules.begin(), rules.end(), [](Rule& a, Rule& b) { return a.priority > b.priority; }); 32 | } 33 | 34 | std::string& RegexCollection::find_match(std::string& value, bool& matched_any) { 35 | for (auto& rule : rules) { 36 | if (std::regex_search(value, rule.rule)) { 37 | matched_any = true; 38 | return rule.repr; 39 | } 40 | } 41 | 42 | return value; 43 | } 44 | 45 | std::string& RegexCollection::get(std::string& value, bool& matched_any) { 46 | if (regex_cache.contains(value)) { 47 | return regex_cache[value]; 48 | } 49 | 50 | // std::string repr = 51 | // waybar::util::find_match(value, window_rewrite_rules_, matched_any); 52 | 53 | std::string repr = find_match(value, matched_any); 54 | 55 | if (!matched_any) { 56 | repr = default_repr; 57 | } 58 | 59 | regex_cache.emplace(value, repr); 60 | 61 | return regex_cache[value]; // Necessary in order to return a reference to the heap 62 | } 63 | 64 | std::string& RegexCollection::get(std::string& value) { 65 | bool matched_any = false; 66 | return get(value, matched_any); 67 | } 68 | 69 | } // namespace waybar::util 70 | -------------------------------------------------------------------------------- /man/waybar-image.5.scd: -------------------------------------------------------------------------------- 1 | waybar-image(5) 2 | 3 | # NAME 4 | 5 | waybar - image module 6 | 7 | # DESCRIPTION 8 | 9 | The *image* module displays an image from a path. 10 | 11 | # CONFIGURATION 12 | 13 | *path*: ++ 14 | typeof: string ++ 15 | The path to the image. 16 | 17 | *exec*: ++ 18 | typeof: string ++ 19 | The path to the script, which should return image path file. ++ 20 | It will only execute if the path is not set 21 | 22 | *size*: ++ 23 | typeof: integer ++ 24 | The width/height to render the image. 25 | 26 | *interval*: ++ 27 | typeof: integer ++ 28 | The interval (in seconds) to re-render the image. ++ 29 | This is useful if the contents of *path* changes. ++ 30 | If no *interval* is defined, the image will only be rendered once. 31 | 32 | *signal*: ++ 33 | typeof: integer ++ 34 | The signal number used to update the module. ++ 35 | This can be used instead of *interval* if the file changes irregularly. ++ 36 | The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*. 37 | 38 | *on-click*: ++ 39 | typeof: string ++ 40 | Command to execute when clicked on the module. 41 | 42 | *on-click-middle*: ++ 43 | typeof: string ++ 44 | Command to execute when middle-clicked on the module using mousewheel. 45 | 46 | *on-update*: ++ 47 | typeof: string ++ 48 | Command to execute when the module is updated. 49 | 50 | *on-scroll-up*: ++ 51 | typeof: string ++ 52 | Command to execute when scrolling up on the module. 53 | 54 | *on-scroll-down*: ++ 55 | typeof: string ++ 56 | Command to execute when scrolling down on the module. 57 | 58 | *smooth-scrolling-threshold*: ++ 59 | typeof: double ++ 60 | Threshold to be used when scrolling. 61 | 62 | *tooltip*: ++ 63 | typeof: bool ++ 64 | default: true ++ 65 | Option to enable tooltip on hover. 66 | 67 | # SCRIPT OUTPUT 68 | 69 | Similar to the *custom* module, output values of the script are *newline* separated. 70 | The following is the output format: 71 | 72 | ``` 73 | $path\\n$tooltip 74 | ``` 75 | 76 | # EXAMPLES 77 | 78 | ``` 79 | "image#album-art": { 80 | "path": "/tmp/mpd_art", 81 | "size": 32, 82 | "interval": 5, 83 | "on-click": "mpc toggle" 84 | } 85 | ``` 86 | 87 | # STYLE 88 | 89 | - *#image* 90 | - *#image.empty* 91 | -------------------------------------------------------------------------------- /include/util/date.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if HAVE_CHRONO_TIMEZONES 6 | #include 7 | #else 8 | #include 9 | #include 10 | 11 | #include 12 | #endif 13 | 14 | // Date 15 | namespace date { 16 | #if HAVE_CHRONO_TIMEZONES 17 | using namespace std::chrono; 18 | using namespace std; 19 | #else 20 | 21 | using system_clock = std::chrono::system_clock; 22 | using seconds = std::chrono::seconds; 23 | 24 | template 25 | inline auto format(const char* spec, const T& arg) { 26 | return date::format(std::regex_replace(spec, std::regex("\\{:L|\\}"), ""), arg); 27 | } 28 | 29 | template 30 | inline auto format(const std::locale& loc, const char* spec, const T& arg) { 31 | return date::format(loc, std::regex_replace(spec, std::regex("\\{:L|\\}"), ""), arg); 32 | } 33 | #endif 34 | } // namespace date 35 | 36 | // Format 37 | namespace waybar::util::date::format { 38 | #if HAVE_CHRONO_TIMEZONES 39 | using namespace std; 40 | #else 41 | using namespace fmt; 42 | #endif 43 | } // namespace waybar::util::date::format 44 | 45 | #if not HAVE_CHRONO_TIMEZONES 46 | template 47 | struct fmt::formatter> { 48 | std::string_view specs; 49 | 50 | template 51 | constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 52 | auto it = ctx.begin(); 53 | if (it != ctx.end() && *it == ':') { 54 | ++it; 55 | } 56 | auto end = it; 57 | while (end != ctx.end() && *end != '}') { 58 | ++end; 59 | } 60 | if (end != it) { 61 | specs = {it, std::string_view::size_type(end - it)}; 62 | } 63 | return end; 64 | } 65 | 66 | template 67 | auto format(const date::zoned_time& ztime, FormatContext& ctx) { 68 | if (ctx.locale()) { 69 | const auto loc = ctx.locale().template get(); 70 | return fmt::format_to(ctx.out(), "{}", date::format(loc, fmt::to_string(specs), ztime)); 71 | } 72 | return fmt::format_to(ctx.out(), "{}", date::format(fmt::to_string(specs), ztime)); 73 | } 74 | }; 75 | #endif 76 | 77 | using namespace date; 78 | -------------------------------------------------------------------------------- /src/modules/load.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/load.hpp" 2 | 3 | // In the 80000 version of fmt library authors decided to optimize imports 4 | // and moved declarations required for fmt::dynamic_format_arg_store in new 5 | // header fmt/args.h 6 | #if (FMT_VERSION >= 80000) 7 | #include 8 | #else 9 | #include 10 | #endif 11 | 12 | waybar::modules::Load::Load(const std::string& id, const Json::Value& config) 13 | : ALabel(config, "load", id, "{load1}", 10) { 14 | thread_ = [this] { 15 | dp.emit(); 16 | thread_.sleep_for(interval_); 17 | }; 18 | } 19 | 20 | auto waybar::modules::Load::update() -> void { 21 | // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both 22 | auto [load1, load5, load15] = Load::getLoad(); 23 | if (tooltipEnabled()) { 24 | auto tooltip = fmt::format("Load 1: {}\nLoad 5: {}\nLoad 15: {}", load1, load5, load15); 25 | label_.set_tooltip_text(tooltip); 26 | } 27 | auto format = format_; 28 | auto state = getState(load1); 29 | if (!state.empty() && config_["format-" + state].isString()) { 30 | format = config_["format-" + state].asString(); 31 | } 32 | 33 | if (format.empty()) { 34 | event_box_.hide(); 35 | } else { 36 | event_box_.show(); 37 | auto icons = std::vector{state}; 38 | fmt::dynamic_format_arg_store store; 39 | store.push_back(fmt::arg("load1", load1)); 40 | store.push_back(fmt::arg("load5", load5)); 41 | store.push_back(fmt::arg("load15", load15)); 42 | store.push_back(fmt::arg("icon1", getIcon(load1, icons))); 43 | store.push_back(fmt::arg("icon5", getIcon(load5, icons))); 44 | store.push_back(fmt::arg("icon15", getIcon(load15, icons))); 45 | label_.set_markup(fmt::vformat(format, store)); 46 | } 47 | 48 | // Call parent update 49 | ALabel::update(); 50 | } 51 | 52 | std::tuple waybar::modules::Load::getLoad() { 53 | double load[3]; 54 | if (getloadavg(load, 3) != -1) { 55 | double load1 = std::ceil(load[0] * 100.0) / 100.0; 56 | double load5 = std::ceil(load[1] * 100.0) / 100.0; 57 | double load15 = std::ceil(load[2] * 100.0) / 100.0; 58 | return {load1, load5, load15}; 59 | } 60 | throw std::runtime_error("Can't get system load"); 61 | } 62 | -------------------------------------------------------------------------------- /protocol/dbus-status-notifier-item.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /man/waybar-backlight-slider.5.scd: -------------------------------------------------------------------------------- 1 | waybar-backlight-slider(5) 2 | 3 | # NAME 4 | 5 | waybar - backlight slider module 6 | 7 | # DESCRIPTION 8 | 9 | The *backlight slider* module displays and controls the current brightness of the default or preferred device. 10 | 11 | The brightness can be controlled by dragging the slider across the bar or clicking on a specific position. 12 | 13 | # CONFIGURATION 14 | 15 | *min*: ++ 16 | typeof: int ++ 17 | default: 0 ++ 18 | The minimum volume value the slider should display and set. 19 | 20 | *max*: ++ 21 | typeof: int ++ 22 | default: 100 ++ 23 | The maximum volume value the slider should display and set. 24 | 25 | *orientation*: ++ 26 | typeof: string ++ 27 | default: horizontal ++ 28 | The orientation of the slider. Can be either `horizontal` or `vertical`. 29 | 30 | *device*: ++ 31 | typeof: string ++ 32 | The name of the preferred device to control. If left empty, a device will be chosen automatically. 33 | 34 | # EXAMPLES 35 | 36 | ``` 37 | "modules-right": [ 38 | "backlight-slider", 39 | ], 40 | "backlight/slider": { 41 | "min": 0, 42 | "max": 100, 43 | "orientation": "horizontal", 44 | "device": "intel_backlight" 45 | } 46 | ``` 47 | 48 | # STYLE 49 | 50 | The slider is a component with multiple CSS Nodes, of which the following are exposed: 51 | 52 | *#backlight-slider*: ++ 53 | Controls the style of the box *around* the slider and bar. 54 | 55 | *#backlight-slider slider*: ++ 56 | Controls the style of the slider handle. 57 | 58 | *#backlight-slider trough*: ++ 59 | Controls the style of the part of the bar that has not been filled. 60 | 61 | *#backlight-slider highlight*: ++ 62 | Controls the style of the part of the bar that has been filled. 63 | 64 | ## STYLE EXAMPLE 65 | 66 | ``` 67 | #backlight-slider slider { 68 | min-height: 0px; 69 | min-width: 0px; 70 | opacity: 0; 71 | background-image: none; 72 | border: none; 73 | box-shadow: none; 74 | } 75 | 76 | #backlight-slider trough { 77 | min-height: 80px; 78 | min-width: 10px; 79 | border-radius: 5px; 80 | background-color: black; 81 | } 82 | 83 | #backlight-slider highlight { 84 | min-width: 10px; 85 | border-radius: 5px; 86 | background-color: red; 87 | } 88 | ``` 89 | -------------------------------------------------------------------------------- /man/waybar-gamemode.5.scd: -------------------------------------------------------------------------------- 1 | waybar-gamemode(5) 2 | 3 | # NAME 4 | 5 | waybar - gamemode module 6 | 7 | # DESCRIPTION 8 | 9 | The *gamemode* module displays if any game or application is running with ++ 10 | Feral Gamemode optimizations. 11 | 12 | # CONFIGURATION 13 | 14 | *format*: ++ 15 | typeof: string ++ 16 | default: {glyph} ++ 17 | The text format. 18 | 19 | *format-alt*: ++ 20 | typeof: string ++ 21 | default: {glyph} {count} ++ 22 | The text format when toggled. 23 | 24 | *tooltip*: ++ 25 | typeof: bool ++ 26 | default: true ++ 27 | Option to disable tooltip on hover. 28 | 29 | *tooltip-format*: ++ 30 | typeof: string ++ 31 | default: Games running: {glyph} ++ 32 | The text format of the tooltip. 33 | 34 | *hide-not-running*: ++ 35 | typeof: bool ++ 36 | default: true ++ 37 | Defines if the module should be hidden if no games are running. 38 | 39 | *use-icon*: ++ 40 | typeof: bool ++ 41 | default: true ++ 42 | Defines if the module should display a GTK icon instead of the specified *glyph* 43 | 44 | *glyph*: ++ 45 | typeof: string ++ 46 | default:  ++ 47 | The string icon to display. Only visible if *use-icon* is set to false. 48 | 49 | *icon-name*: ++ 50 | typeof: string ++ 51 | default: input-gaming-symbolic ++ 52 | The GTK icon to display. Only visible if *use-icon* is set to true. 53 | 54 | *icon-size*: ++ 55 | typeof: unsigned integer ++ 56 | default: 20 ++ 57 | Defines the size of the icons. 58 | 59 | *icon-spacing*: ++ 60 | typeof: unsigned integer ++ 61 | default: 4 ++ 62 | Defines the spacing between the icon and the text. 63 | 64 | # FORMAT REPLACEMENTS 65 | 66 | *{glyph}*: The string icon glyph to use instead. 67 | 68 | *{count}*: The number of games running with gamemode optimizations. 69 | 70 | # TOOLTIP FORMAT REPLACEMENTS 71 | 72 | *{count}*: The number of games running with gamemode optimizations. 73 | 74 | # EXAMPLES 75 | 76 | ``` 77 | "gamemode": { 78 | "format": "{glyph}", 79 | "format-alt": "{glyph} {count}", 80 | "glyph": "", 81 | "hide-not-running": true, 82 | "use-icon": true, 83 | "icon-name": "input-gaming-symbolic", 84 | "icon-spacing": 4, 85 | "icon-size": 20, 86 | "tooltip": true, 87 | "tooltip-format": "Games running: {count}" 88 | } 89 | 90 | ``` 91 | 92 | # STYLE 93 | 94 | - *#gamemode* 95 | - *#gamemode.running* 96 | -------------------------------------------------------------------------------- /include/modules/sway/workspaces.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "AModule.hpp" 11 | #include "bar.hpp" 12 | #include "client.hpp" 13 | #include "modules/sway/ipc/client.hpp" 14 | #include "util/json.hpp" 15 | #include "util/regex_collection.hpp" 16 | 17 | namespace waybar::modules::sway { 18 | 19 | class Workspaces : public AModule, public sigc::trackable { 20 | public: 21 | Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); 22 | virtual ~Workspaces() = default; 23 | auto update() -> void override; 24 | 25 | private: 26 | static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\""; 27 | static constexpr std::string_view persistent_workspace_switch_cmd_ = 28 | R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")"; 29 | 30 | static int convertWorkspaceNameToNum(std::string name); 31 | static int windowRewritePriorityFunction(std::string const& window_rule); 32 | 33 | void onCmd(const struct Ipc::ipc_response&); 34 | void onEvent(const struct Ipc::ipc_response&); 35 | bool filterButtons(); 36 | static bool hasFlag(const Json::Value&, const std::string&); 37 | void updateWindows(const Json::Value&, std::string&); 38 | Gtk::Button& addButton(const Json::Value&); 39 | void onButtonReady(const Json::Value&, Gtk::Button&); 40 | std::string getIcon(const std::string&, const Json::Value&); 41 | const std::string getCycleWorkspace(std::vector::iterator, bool prev) const; 42 | uint16_t getWorkspaceIndex(const std::string& name) const; 43 | std::string trimWorkspaceName(std::string); 44 | bool handleScroll(GdkEventScroll*) override; 45 | 46 | const Bar& bar_; 47 | std::vector workspaces_; 48 | std::vector high_priority_named_; 49 | std::vector workspaces_order_; 50 | Gtk::Box box_; 51 | std::string m_formatWindowSeperator; 52 | std::string m_windowRewriteDefault; 53 | util::RegexCollection m_windowRewriteRules; 54 | util::JsonParser parser_; 55 | std::unordered_map buttons_; 56 | std::mutex mutex_; 57 | Ipc ipc_; 58 | }; 59 | 60 | } // namespace waybar::modules::sway 61 | -------------------------------------------------------------------------------- /include/client.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bar.hpp" 9 | #include "config.hpp" 10 | #include "util/css_reload_helper.hpp" 11 | #include "util/portal.hpp" 12 | 13 | struct zwp_idle_inhibitor_v1; 14 | struct zwp_idle_inhibit_manager_v1; 15 | 16 | namespace waybar { 17 | 18 | class Client { 19 | public: 20 | static Client *inst(); 21 | int main(int argc, char *argv[]); 22 | void reset(); 23 | 24 | Glib::RefPtr gtk_app; 25 | Glib::RefPtr gdk_display; 26 | struct wl_display *wl_display = nullptr; 27 | struct wl_registry *registry = nullptr; 28 | struct zxdg_output_manager_v1 *xdg_output_manager = nullptr; 29 | struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr; 30 | std::vector> bars; 31 | Config config; 32 | std::string bar_id; 33 | 34 | private: 35 | Client() = default; 36 | const std::string getStyle(const std::string &style, std::optional appearance); 37 | void bindInterfaces(); 38 | void handleOutput(struct waybar_output &output); 39 | auto setupCss(const std::string &css_file) -> void; 40 | struct waybar_output &getOutput(void *); 41 | std::vector getOutputConfigs(struct waybar_output &output); 42 | 43 | static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name, 44 | const char *interface, uint32_t version); 45 | static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name); 46 | static void handleOutputDone(void *, struct zxdg_output_v1 *); 47 | static void handleOutputName(void *, struct zxdg_output_v1 *, const char *); 48 | static void handleOutputDescription(void *, struct zxdg_output_v1 *, const char *); 49 | void handleMonitorAdded(Glib::RefPtr monitor); 50 | void handleMonitorRemoved(Glib::RefPtr monitor); 51 | void handleDeferredMonitorRemoval(Glib::RefPtr monitor); 52 | 53 | Glib::RefPtr style_context_; 54 | Glib::RefPtr css_provider_; 55 | std::unique_ptr portal; 56 | std::list outputs_; 57 | std::unique_ptr m_cssReloadHelper; 58 | std::string m_cssFile; 59 | }; 60 | 61 | } // namespace waybar 62 | -------------------------------------------------------------------------------- /src/modules/pulseaudio_slider.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/pulseaudio_slider.hpp" 2 | 3 | namespace waybar::modules { 4 | 5 | PulseaudioSlider::PulseaudioSlider(const std::string& id, const Json::Value& config) 6 | : ASlider(config, "pulseaudio-slider", id) { 7 | backend = util::AudioBackend::getInstance([this] { this->dp.emit(); }); 8 | backend->setIgnoredSinks(config_["ignored-sinks"]); 9 | 10 | if (config_["target"].isString()) { 11 | std::string target = config_["target"].asString(); 12 | if (target == "sink") { 13 | this->target = PulseaudioSliderTarget::Sink; 14 | } else if (target == "source") { 15 | this->target = PulseaudioSliderTarget::Source; 16 | } 17 | } 18 | } 19 | 20 | void PulseaudioSlider::update() { 21 | switch (target) { 22 | case PulseaudioSliderTarget::Sink: 23 | if (backend->getSinkMuted()) { 24 | scale_.set_value(min_); 25 | } else { 26 | scale_.set_value(backend->getSinkVolume()); 27 | } 28 | break; 29 | 30 | case PulseaudioSliderTarget::Source: 31 | if (backend->getSourceMuted()) { 32 | scale_.set_value(min_); 33 | } else { 34 | scale_.set_value(backend->getSourceVolume()); 35 | } 36 | break; 37 | } 38 | } 39 | 40 | void PulseaudioSlider::onValueChanged() { 41 | bool is_mute = false; 42 | 43 | switch (target) { 44 | case PulseaudioSliderTarget::Sink: 45 | if (backend->getSinkMuted()) { 46 | is_mute = true; 47 | } 48 | break; 49 | 50 | case PulseaudioSliderTarget::Source: 51 | if (backend->getSourceMuted()) { 52 | is_mute = true; 53 | } 54 | break; 55 | } 56 | 57 | uint16_t volume = scale_.get_value(); 58 | 59 | if (is_mute) { 60 | // Avoid setting sink/source to volume 0 if the user muted if via another mean. 61 | if (volume == 0) { 62 | return; 63 | } 64 | 65 | // If the sink/source is mute, but the user clicked the slider, unmute it! 66 | else { 67 | switch (target) { 68 | case PulseaudioSliderTarget::Sink: 69 | backend->toggleSinkMute(false); 70 | break; 71 | 72 | case PulseaudioSliderTarget::Source: 73 | backend->toggleSourceMute(false); 74 | break; 75 | } 76 | } 77 | } 78 | 79 | backend->changeVolume(volume, min_, max_); 80 | } 81 | 82 | } // namespace waybar::modules -------------------------------------------------------------------------------- /man/waybar-sndio.5.scd: -------------------------------------------------------------------------------- 1 | waybar-sndio(5) 2 | 3 | # NAME 4 | 5 | waybar - sndio module 6 | 7 | # DESCRIPTION 8 | 9 | The *sndio* module displays the current volume reported by sndio(7). 10 | 11 | Additionally, you can control the volume by scrolling *up* or *down* while the 12 | cursor is over the module, and clicking on the module toggles mute. 13 | 14 | # CONFIGURATION 15 | 16 | *format*: ++ 17 | typeof: string ++ 18 | default: {volume}% ++ 19 | The format for how information should be displayed. 20 | 21 | *rotate*: ++ 22 | typeof: integer ++ 23 | Positive value to rotate the text label. 24 | 25 | *max-length*: ++ 26 | typeof: integer ++ 27 | The maximum length in character the module should display. 28 | 29 | *min-length*: ++ 30 | typeof: integer ++ 31 | The minimum length in characters the module should accept. 32 | 33 | *align*: ++ 34 | typeof: float ++ 35 | 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. 36 | 37 | *scroll-step*: ++ 38 | typeof: int ++ 39 | default: 5 ++ 40 | The speed at which to change the volume when scrolling. 41 | 42 | *on-click*: ++ 43 | typeof: string ++ 44 | Command to execute when clicked on the module. 45 | This replaces the default behaviour of toggling mute. 46 | 47 | *on-click-middle*: ++ 48 | typeof: string ++ 49 | Command to execute when middle-clicked on the module using mousewheel. 50 | 51 | *on-click-right*: ++ 52 | typeof: string ++ 53 | Command to execute when you right-click on the module. 54 | 55 | *on-update*: ++ 56 | typeof: string ++ 57 | Command to execute when the module is updated. 58 | 59 | *on-scroll-up*: ++ 60 | typeof: string ++ 61 | Command to execute when scrolling up on the module. ++ 62 | This replaces the default behaviour of volume control. 63 | 64 | *on-scroll-down*: ++ 65 | typeof: string ++ 66 | Command to execute when scrolling down on the module. ++ 67 | This replaces the default behaviour of volume control. 68 | 69 | *smooth-scrolling-threshold*: ++ 70 | typeof: double ++ 71 | Threshold to be used when scrolling. 72 | 73 | # FORMAT REPLACEMENTS 74 | 75 | *{volume}*: Volume in percentage. 76 | 77 | *{raw_value}*: Volume as value reported by sndio. 78 | 79 | # EXAMPLES 80 | 81 | ``` 82 | "sndio": { 83 | "format": "{raw_value} 🎜", 84 | "scroll-step": 3 85 | } 86 | ``` 87 | 88 | # STYLE 89 | 90 | - *#sndio* 91 | - *#sndio.muted* 92 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Highly customizable Wayland bar for Sway and Wlroots based compositors"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 | flake-compat = { 7 | url = "github:edolstra/flake-compat"; 8 | flake = false; 9 | }; 10 | }; 11 | 12 | outputs = { self, nixpkgs, ... }: 13 | let 14 | inherit (nixpkgs) lib; 15 | genSystems = func: lib.genAttrs [ 16 | "x86_64-linux" 17 | "aarch64-linux" 18 | ] 19 | (system: func (import nixpkgs { inherit system; })); 20 | 21 | mkDate = longDate: (lib.concatStringsSep "-" [ 22 | (builtins.substring 0 4 longDate) 23 | (builtins.substring 4 2 longDate) 24 | (builtins.substring 6 2 longDate) 25 | ]); 26 | in 27 | { 28 | devShells = genSystems 29 | (pkgs: 30 | { 31 | default = 32 | pkgs.mkShell 33 | { 34 | name = "waybar-shell"; 35 | 36 | # inherit attributes from upstream nixpkgs derivation 37 | inherit (pkgs.waybar) buildInputs depsBuildBuild depsBuildBuildPropagated depsBuildTarget 38 | depsBuildTargetPropagated depsHostHost depsHostHostPropagated depsTargetTarget 39 | depsTargetTargetPropagated propagatedBuildInputs propagatedNativeBuildInputs strictDeps; 40 | 41 | # overrides for local development 42 | nativeBuildInputs = pkgs.waybar.nativeBuildInputs ++ (with pkgs; [ 43 | clang-tools 44 | gdb 45 | ]); 46 | }; 47 | }); 48 | 49 | overlays.default = final: prev: { 50 | waybar = final.callPackage ./nix/default.nix { 51 | # take the first "version: '...'" from meson.build 52 | version = 53 | (builtins.head (builtins.split "'" 54 | (builtins.elemAt 55 | (builtins.split " version: '" (builtins.readFile ./meson.build)) 56 | 2))) 57 | + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); 58 | }; 59 | }; 60 | 61 | packages = genSystems (pkgs: 62 | let packages = self.overlays.default pkgs pkgs; 63 | in packages // { 64 | default = packages.waybar; 65 | }); 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /man/waybar-wlr-workspaces.5.scd: -------------------------------------------------------------------------------- 1 | waybar-wlr-workspaces(5) 2 | 3 | # NAME 4 | 5 | waybar - wlr workspaces module 6 | 7 | # DESCRIPTION 8 | 9 | The *workspaces* module displays the currently used workspaces in wayland compositor. 10 | 11 | # CONFIGURATION 12 | 13 | Addressed by *wlr/workspaces* 14 | 15 | *format*: ++ 16 | typeof: string ++ 17 | default: {name} ++ 18 | The format, how information should be displayed. 19 | 20 | *format-icons*: ++ 21 | typeof: array ++ 22 | Based on the workspace name and state, the corresponding icon gets selected. See *icons*. 23 | 24 | *sort-by-name*: ++ 25 | typeof: bool ++ 26 | default: true ++ 27 | Should workspaces be sorted by name. 28 | 29 | *sort-by-coordinates*: ++ 30 | typeof: bool ++ 31 | default: true ++ 32 | Should workspaces be sorted by coordinates. ++ 33 | Note that if both *sort-by-name* and *sort-by-coordinates* are true sort-by name will be first. If both are false - sort by id will be performed. 34 | 35 | *sort-by-number*: ++ 36 | typeof: bool ++ 37 | default: false ++ 38 | If set to true, workspace names will be sorted numerically. Takes precedence over any other sort-by option. 39 | 40 | *all-outputs*: ++ 41 | typeof: bool ++ 42 | default: false ++ 43 | If set to false workspaces group will be shown only in assigned output. Otherwise, all workspace groups are shown. 44 | 45 | *active-only*: ++ 46 | typeof: bool ++ 47 | default: false ++ 48 | If set to true only active or urgent workspaces will be shown. 49 | 50 | # FORMAT REPLACEMENTS 51 | 52 | *{name}*: Name of workspace assigned by compositor 53 | 54 | *{icon}*: Icon, as defined in *format-icons*. 55 | 56 | # CLICK ACTIONS 57 | 58 | *activate*: Switch to workspace. 59 | 60 | *close*: Close the workspace. 61 | 62 | # ICONS 63 | 64 | In addition to workspace name matching, the following *format-icons* can be set. 65 | 66 | - *default*: Will be shown, when no string match is found. 67 | - *active*: Will be shown, when workspace is active 68 | 69 | # EXAMPLES 70 | 71 | ``` 72 | "wlr/workspaces": { 73 | "format": "{name}: {icon}", 74 | "format-icons": { 75 | "1": "", 76 | "2": "", 77 | "3": "", 78 | "4": "", 79 | "5": "", 80 | "active": "", 81 | "default": "" 82 | }, 83 | "sort-by-number": true 84 | } 85 | ``` 86 | 87 | # Style 88 | 89 | - *#workspaces* 90 | - *#workspaces button* 91 | - *#workspaces button.active* 92 | - *#workspaces button.urgent* 93 | - *#workspaces button.hidden* 94 | -------------------------------------------------------------------------------- /src/modules/image.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/image.hpp" 2 | 3 | waybar::modules::Image::Image(const std::string& id, const Json::Value& config) 4 | : AModule(config, "image", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0) { 5 | box_.pack_start(image_); 6 | box_.set_name("image"); 7 | if (!id.empty()) { 8 | box_.get_style_context()->add_class(id); 9 | } 10 | box_.get_style_context()->add_class(MODULE_CLASS); 11 | event_box_.add(box_); 12 | 13 | dp.emit(); 14 | 15 | size_ = config["size"].asInt(); 16 | 17 | interval_ = config_["interval"].asInt(); 18 | 19 | if (size_ == 0) { 20 | size_ = 16; 21 | } 22 | 23 | if (interval_ == 0) { 24 | interval_ = INT_MAX; 25 | } 26 | 27 | delayWorker(); 28 | } 29 | 30 | void waybar::modules::Image::delayWorker() { 31 | thread_ = [this] { 32 | dp.emit(); 33 | auto interval = std::chrono::seconds(interval_); 34 | thread_.sleep_for(interval); 35 | }; 36 | } 37 | 38 | void waybar::modules::Image::refresh(int sig) { 39 | if (sig == SIGRTMIN + config_["signal"].asInt()) { 40 | thread_.wake_up(); 41 | } 42 | } 43 | 44 | auto waybar::modules::Image::update() -> void { 45 | Glib::RefPtr pixbuf; 46 | if (config_["path"].isString()) { 47 | path_ = config_["path"].asString(); 48 | } else if (config_["exec"].isString()) { 49 | output_ = util::command::exec(config_["exec"].asString(), ""); 50 | parseOutputRaw(); 51 | } else { 52 | path_ = ""; 53 | } 54 | if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) 55 | pixbuf = Gdk::Pixbuf::create_from_file(path_, size_, size_); 56 | else 57 | pixbuf = {}; 58 | 59 | if (pixbuf) { 60 | if (tooltipEnabled() && !tooltip_.empty()) { 61 | if (box_.get_tooltip_markup() != tooltip_) { 62 | box_.set_tooltip_markup(tooltip_); 63 | } 64 | } 65 | image_.set(pixbuf); 66 | image_.show(); 67 | box_.get_style_context()->remove_class("empty"); 68 | } else { 69 | image_.clear(); 70 | image_.hide(); 71 | box_.get_style_context()->add_class("empty"); 72 | } 73 | 74 | AModule::update(); 75 | } 76 | 77 | void waybar::modules::Image::parseOutputRaw() { 78 | std::istringstream output(output_.out); 79 | std::string line; 80 | int i = 0; 81 | while (getline(output, line)) { 82 | if (i == 0) { 83 | path_ = line; 84 | } else if (i == 1) { 85 | tooltip_ = line; 86 | } else { 87 | break; 88 | } 89 | i++; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/modules/cpu.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/cpu.hpp" 2 | 3 | #include "modules/cpu_frequency.hpp" 4 | #include "modules/cpu_usage.hpp" 5 | #include "modules/load.hpp" 6 | 7 | // In the 80000 version of fmt library authors decided to optimize imports 8 | // and moved declarations required for fmt::dynamic_format_arg_store in new 9 | // header fmt/args.h 10 | #if (FMT_VERSION >= 80000) 11 | #include 12 | #else 13 | #include 14 | #endif 15 | 16 | waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config) 17 | : ALabel(config, "cpu", id, "{usage}%", 10) { 18 | thread_ = [this] { 19 | dp.emit(); 20 | thread_.sleep_for(interval_); 21 | }; 22 | } 23 | 24 | auto waybar::modules::Cpu::update() -> void { 25 | // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both 26 | auto [load1, load5, load15] = Load::getLoad(); 27 | auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_); 28 | auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); 29 | if (tooltipEnabled()) { 30 | label_.set_tooltip_text(tooltip); 31 | } 32 | auto format = format_; 33 | auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; 34 | auto state = getState(total_usage); 35 | if (!state.empty() && config_["format-" + state].isString()) { 36 | format = config_["format-" + state].asString(); 37 | } 38 | 39 | if (format.empty()) { 40 | event_box_.hide(); 41 | } else { 42 | event_box_.show(); 43 | auto icons = std::vector{state}; 44 | fmt::dynamic_format_arg_store store; 45 | store.push_back(fmt::arg("load", load1)); 46 | store.push_back(fmt::arg("usage", total_usage)); 47 | store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); 48 | store.push_back(fmt::arg("max_frequency", max_frequency)); 49 | store.push_back(fmt::arg("min_frequency", min_frequency)); 50 | store.push_back(fmt::arg("avg_frequency", avg_frequency)); 51 | for (size_t i = 1; i < cpu_usage.size(); ++i) { 52 | auto core_i = i - 1; 53 | auto core_format = fmt::format("usage{}", core_i); 54 | store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); 55 | auto icon_format = fmt::format("icon{}", core_i); 56 | store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); 57 | } 58 | label_.set_markup(fmt::vformat(format, store)); 59 | } 60 | 61 | // Call parent update 62 | ALabel::update(); 63 | } 64 | -------------------------------------------------------------------------------- /include/util/SafeSignal.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace waybar { 15 | 16 | /** 17 | * Thread-safe signal wrapper. 18 | * Uses Glib::Dispatcher to pass events to another thread and locked queue to pass the arguments. 19 | */ 20 | template 21 | struct SafeSignal : sigc::signal...)> { 22 | public: 23 | SafeSignal() { dp_.connect(sigc::mem_fun(*this, &SafeSignal::handle_event)); } 24 | 25 | template 26 | void emit(EmitArgs&&... args) { 27 | if (main_tid_ == std::this_thread::get_id()) { 28 | /* 29 | * Bypass the queue if the method is called the main thread. 30 | * Ensures that events emitted from the main thread are processed synchronously and saves a 31 | * few CPU cycles on locking/queuing. 32 | * As a downside, this makes main thread events prioritized over the other threads and 33 | * disrupts chronological order. 34 | */ 35 | signal_t::emit(std::forward(args)...); 36 | } else { 37 | { 38 | std::unique_lock lock(mutex_); 39 | queue_.emplace(std::forward(args)...); 40 | } 41 | dp_.emit(); 42 | } 43 | } 44 | 45 | template 46 | inline void operator()(EmitArgs&&... args) { 47 | emit(std::forward(args)...); 48 | } 49 | 50 | protected: 51 | using signal_t = sigc::signal...)>; 52 | using slot_t = decltype(std::declval().make_slot()); 53 | using arg_tuple_t = std::tuple...>; 54 | // ensure that unwrapped methods are not accessible 55 | using signal_t::emit_reverse; 56 | using signal_t::make_slot; 57 | 58 | void handle_event() { 59 | for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) { 60 | auto args = queue_.front(); 61 | queue_.pop(); 62 | lock.unlock(); 63 | std::apply(cached_fn_, args); 64 | } 65 | } 66 | 67 | Glib::Dispatcher dp_; 68 | std::mutex mutex_; 69 | std::queue queue_; 70 | const std::thread::id main_tid_ = std::this_thread::get_id(); 71 | // cache functor for signal emission to avoid recreating it on each event 72 | const slot_t cached_fn_ = make_slot(); 73 | }; 74 | 75 | } // namespace waybar 76 | -------------------------------------------------------------------------------- /man/waybar-backlight.5.scd: -------------------------------------------------------------------------------- 1 | waybar-backlight(5) 2 | 3 | # NAME 4 | 5 | waybar - backlight module 6 | 7 | # DESCRIPTION 8 | 9 | The *backlight* module displays the current backlight level. 10 | 11 | # CONFIGURATION 12 | 13 | *interval*: ++ 14 | typeof: integer ++ 15 | default: 2 ++ 16 | The interval in which information gets polled. 17 | 18 | *format*: ++ 19 | typeof: string ++ 20 | default: {percent}% ++ 21 | The format, how information should be displayed. On {} data gets inserted. 22 | 23 | *max-length*: ++ 24 | typeof: integer ++ 25 | The maximum length in characters the module should display. 26 | 27 | *min-length*: ++ 28 | typeof: integer ++ 29 | The minimum length in characters the module should accept. 30 | 31 | *align*: ++ 32 | typeof: float ++ 33 | 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. 34 | 35 | *rotate*: ++ 36 | typeof: integer ++ 37 | Positive value to rotate the text label. 38 | 39 | *states*: ++ 40 | typeof: object ++ 41 | A number of backlight states which get activated on certain brightness levels. See *waybar-states(5)*. 42 | 43 | *on-click*: ++ 44 | typeof: string ++ 45 | Command to execute when the module is clicked. 46 | 47 | *on-click-middle*: ++ 48 | typeof: string ++ 49 | Command to execute when middle-clicked on the module using mouse scroll wheel. 50 | 51 | *on-click-right*: ++ 52 | typeof: string ++ 53 | Command to execute when the module is right-clicked. 54 | 55 | *on-update*: ++ 56 | typeof: string ++ 57 | Command to execute when the module is updated. 58 | 59 | *on-scroll-up*: ++ 60 | typeof: string ++ 61 | Command to execute when performing a scroll up on the module. This replaces the default behaviour of brightness control. 62 | 63 | *on-scroll-down*: ++ 64 | typeof: string 65 | Command to execute when performing a scroll down on the module. This replaces the default behaviour of brightness control. 66 | 67 | *smooth-scrolling-threshold*: ++ 68 | typeof: double 69 | Threshold to be used when scrolling. 70 | 71 | *reverse-scrolling*: ++ 72 | typeof: bool ++ 73 | Option to reverse the scroll direction. 74 | 75 | *scroll-step*: ++ 76 | typeof: float ++ 77 | default: 1.0 ++ 78 | The speed at which to change the brightness when scrolling. 79 | 80 | # EXAMPLE: 81 | 82 | ``` 83 | "backlight": { 84 | "device": "intel_backlight", 85 | "format": "{percent}% {icon}", 86 | "format-icons": ["", ""] 87 | } 88 | ``` 89 | 90 | # STYLE 91 | 92 | - *#backlight* 93 | -------------------------------------------------------------------------------- /man/waybar-inhibitor.5.scd: -------------------------------------------------------------------------------- 1 | waybar-inhibitor(5) 2 | 3 | # NAME 4 | 5 | waybar - inhibitor module 6 | 7 | # DESCRIPTION 8 | 9 | The *inhibitor* module allows one to take an inhibitor lock that logind provides. 10 | See *systemd-inhibit*(1) for more information. 11 | 12 | # CONFIGURATION 13 | 14 | *what*: ++ 15 | typeof: string or array ++ 16 | The inhibitor lock or locks that should be taken when active. The available inhibitor locks are *idle*, *shutdown*, *sleep*, *handle-power-key*, *handle-suspend-key*, *handle-hibernate-key* and *handle-lid-switch*. 17 | 18 | *format*: ++ 19 | typeof: string ++ 20 | The format, how the state should be displayed. 21 | 22 | *format-icons*: ++ 23 | typeof: array ++ 24 | Based on the current state, the corresponding icon gets selected. 25 | 26 | *rotate*: ++ 27 | typeof: integer ++ 28 | Positive value to rotate the text label. 29 | 30 | *max-length*: ++ 31 | typeof: integer ++ 32 | The maximum length in character the module should display. 33 | 34 | *min-length*: ++ 35 | typeof: integer ++ 36 | The minimum length in characters the module should accept. 37 | 38 | *align*: ++ 39 | typeof: float ++ 40 | 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. 41 | 42 | *on-click*: ++ 43 | typeof: string ++ 44 | Command to execute when clicked on the module. A click also toggles the state 45 | 46 | *on-click-middle*: ++ 47 | typeof: string ++ 48 | Command to execute when middle-clicked on the module using mousewheel. 49 | 50 | *on-click-right*: ++ 51 | typeof: string ++ 52 | Command to execute when you right-click on the module. 53 | 54 | *on-update*: ++ 55 | typeof: string ++ 56 | Command to execute when the module is updated. 57 | 58 | *on-scroll-up*: ++ 59 | typeof: string ++ 60 | Command to execute when scrolling up on the module. 61 | 62 | *on-scroll-down*: ++ 63 | typeof: string ++ 64 | Command to execute when scrolling down on the module. 65 | 66 | *smooth-scrolling-threshold*: ++ 67 | typeof: double ++ 68 | Threshold to be used when scrolling. 69 | 70 | *tooltip*: ++ 71 | typeof: bool ++ 72 | default: true ++ 73 | Option to disable tooltip on hover. 74 | 75 | # FORMAT REPLACEMENTS 76 | 77 | *{status}*: status (*activated* or *deactivated*) 78 | 79 | *{icon}*: Icon, as defined in *format-icons* 80 | 81 | # EXAMPLES 82 | 83 | ``` 84 | "inhibitor": { 85 | "what": "handle-lid-switch", 86 | "format": "{icon}", 87 | "format-icons": { 88 | "activated": "", 89 | "deactivated": "" 90 | } 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /include/modules/network.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "ALabel.hpp" 14 | #include "util/sleeper_thread.hpp" 15 | #ifdef WANT_RFKILL 16 | #include "util/rfkill.hpp" 17 | #endif 18 | 19 | namespace waybar::modules { 20 | 21 | class Network : public ALabel { 22 | public: 23 | Network(const std::string&, const Json::Value&); 24 | virtual ~Network(); 25 | auto update() -> void override; 26 | 27 | private: 28 | static const uint8_t MAX_RETRY = 5; 29 | static const uint8_t EPOLL_MAX = 200; 30 | 31 | static int handleEvents(struct nl_msg*, void*); 32 | static int handleEventsDone(struct nl_msg*, void*); 33 | static int handleScan(struct nl_msg*, void*); 34 | 35 | void askForStateDump(void); 36 | 37 | void worker(); 38 | void createInfoSocket(); 39 | void createEventSocket(); 40 | void parseEssid(struct nlattr**); 41 | void parseSignal(struct nlattr**); 42 | void parseFreq(struct nlattr**); 43 | bool associatedOrJoined(struct nlattr**); 44 | bool checkInterface(std::string name); 45 | auto getInfo() -> void; 46 | const std::string getNetworkState() const; 47 | void clearIface(); 48 | bool wildcardMatch(const std::string& pattern, const std::string& text) const; 49 | std::optional> readBandwidthUsage(); 50 | 51 | int ifid_; 52 | sa_family_t family_; 53 | struct sockaddr_nl nladdr_ = {0}; 54 | struct nl_sock* sock_ = nullptr; 55 | struct nl_sock* ev_sock_ = nullptr; 56 | int efd_; 57 | int ev_fd_; 58 | int nl80211_id_; 59 | std::mutex mutex_; 60 | 61 | bool want_route_dump_; 62 | bool want_link_dump_; 63 | bool want_addr_dump_; 64 | bool dump_in_progress_; 65 | bool is_p2p_; 66 | 67 | unsigned long long bandwidth_down_total_; 68 | unsigned long long bandwidth_up_total_; 69 | 70 | std::string state_; 71 | std::string essid_; 72 | bool carrier_; 73 | std::string ifname_; 74 | std::string ipaddr_; 75 | std::string gwaddr_; 76 | std::string netmask_; 77 | int cidr_; 78 | int32_t signal_strength_dbm_; 79 | uint8_t signal_strength_; 80 | std::string signal_strength_app_; 81 | uint32_t route_priority; 82 | 83 | util::SleeperThread thread_; 84 | util::SleeperThread thread_timer_; 85 | #ifdef WANT_RFKILL 86 | util::Rfkill rfkill_; 87 | #endif 88 | float frequency_; 89 | }; 90 | 91 | } // namespace waybar::modules 92 | -------------------------------------------------------------------------------- /resources/custom_modules/cffi_example/main.c: -------------------------------------------------------------------------------- 1 | 2 | #include "waybar_cffi_module.h" 3 | 4 | typedef struct { 5 | wbcffi_module* waybar_module; 6 | GtkBox* container; 7 | GtkButton* button; 8 | } ExampleMod; 9 | 10 | // This static variable is shared between all instances of this module 11 | static int instance_count = 0; 12 | 13 | void onclicked(GtkButton* button) { 14 | char text[256]; 15 | snprintf(text, 256, "Dice throw result: %d", rand() % 6 + 1); 16 | gtk_button_set_label(button, text); 17 | } 18 | 19 | // You must 20 | const size_t wbcffi_version = 1; 21 | 22 | void* wbcffi_init(const wbcffi_init_info* init_info, const wbcffi_config_entry* config_entries, 23 | size_t config_entries_len) { 24 | printf("cffi_example: init config:\n"); 25 | for (size_t i = 0; i < config_entries_len; i++) { 26 | printf(" %s = %s\n", config_entries[i].key, config_entries[i].value); 27 | } 28 | 29 | // Allocate the instance object 30 | ExampleMod* inst = malloc(sizeof(ExampleMod)); 31 | inst->waybar_module = init_info->obj; 32 | 33 | GtkContainer* root = init_info->get_root_widget(init_info->obj); 34 | 35 | // Add a container for displaying the next widgets 36 | inst->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5)); 37 | gtk_container_add(GTK_CONTAINER(root), GTK_WIDGET(inst->container)); 38 | 39 | // Add a label 40 | GtkLabel* label = GTK_LABEL(gtk_label_new("[Example C FFI Module:")); 41 | gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(label)); 42 | 43 | // Add a button 44 | inst->button = GTK_BUTTON(gtk_button_new_with_label("click me !")); 45 | g_signal_connect(inst->button, "clicked", G_CALLBACK(onclicked), NULL); 46 | gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(inst->button)); 47 | 48 | // Add a label 49 | label = GTK_LABEL(gtk_label_new("]")); 50 | gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(label)); 51 | 52 | // Return instance object 53 | printf("cffi_example inst=%p: init success ! (%d total instances)\n", inst, ++instance_count); 54 | return inst; 55 | } 56 | 57 | void wbcffi_deinit(void* instance) { 58 | printf("cffi_example inst=%p: free memory\n", instance); 59 | free(instance); 60 | } 61 | 62 | void wbcffi_update(void* instance) { printf("cffi_example inst=%p: Update request\n", instance); } 63 | 64 | void wbcffi_refresh(void* instance, int signal) { 65 | printf("cffi_example inst=%p: Received refresh signal %d\n", instance, signal); 66 | } 67 | 68 | void wbcffi_doaction(void* instance, const char* name) { 69 | printf("cffi_example inst=%p: doAction(%s)\n", instance, name); 70 | } -------------------------------------------------------------------------------- /include/modules/bluetooth.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ALabel.hpp" 4 | #ifdef WANT_RFKILL 5 | #include "util/rfkill.hpp" 6 | #endif 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace waybar::modules { 14 | 15 | class Bluetooth : public ALabel { 16 | struct ControllerInfo { 17 | std::string path; 18 | std::string address; 19 | std::string address_type; 20 | // std::string name; // just use alias instead 21 | std::string alias; 22 | bool powered; 23 | bool discoverable; 24 | bool pairable; 25 | bool discovering; 26 | }; 27 | 28 | // NOTE: there are some properties that not all devices provide 29 | struct DeviceInfo { 30 | std::string path; 31 | std::string paired_controller; 32 | std::string address; 33 | std::string address_type; 34 | // std::optional name; // just use alias instead 35 | std::string alias; 36 | std::optional icon; 37 | bool paired; 38 | bool trusted; 39 | bool blocked; 40 | bool connected; 41 | bool services_resolved; 42 | // NOTE: experimental feature in bluez 43 | std::optional battery_percentage; 44 | }; 45 | 46 | public: 47 | Bluetooth(const std::string&, const Json::Value&); 48 | virtual ~Bluetooth() = default; 49 | auto update() -> void override; 50 | 51 | private: 52 | static auto onInterfaceAddedOrRemoved(GDBusObjectManager*, GDBusObject*, GDBusInterface*, 53 | gpointer) -> void; 54 | static auto onInterfaceProxyPropertiesChanged(GDBusObjectManagerClient*, GDBusObjectProxy*, 55 | GDBusProxy*, GVariant*, const gchar* const*, 56 | gpointer) -> void; 57 | 58 | auto getDeviceBatteryPercentage(GDBusObject*) -> std::optional; 59 | auto getDeviceProperties(GDBusObject*, DeviceInfo&) -> bool; 60 | auto getControllerProperties(GDBusObject*, ControllerInfo&) -> bool; 61 | 62 | // Returns std::nullopt if no controller could be found 63 | auto findCurController() -> std::optional; 64 | auto findConnectedDevices(const std::string&, std::vector&) -> void; 65 | 66 | #ifdef WANT_RFKILL 67 | util::Rfkill rfkill_; 68 | #endif 69 | const std::unique_ptr manager_; 70 | 71 | std::string state_; 72 | std::optional cur_controller_; 73 | std::vector connected_devices_; 74 | DeviceInfo cur_focussed_device_; 75 | std::string device_enumerate_; 76 | 77 | std::vector device_preference_; 78 | }; 79 | 80 | } // namespace waybar::modules 81 | -------------------------------------------------------------------------------- /src/modules/cpu_frequency/common.cpp: -------------------------------------------------------------------------------- 1 | #include "modules/cpu_frequency.hpp" 2 | 3 | // In the 80000 version of fmt library authors decided to optimize imports 4 | // and moved declarations required for fmt::dynamic_format_arg_store in new 5 | // header fmt/args.h 6 | #if (FMT_VERSION >= 80000) 7 | #include 8 | #else 9 | #include 10 | #endif 11 | 12 | waybar::modules::CpuFrequency::CpuFrequency(const std::string& id, const Json::Value& config) 13 | : ALabel(config, "cpu_frequency", id, "{avg_frequency}", 10) { 14 | thread_ = [this] { 15 | dp.emit(); 16 | thread_.sleep_for(interval_); 17 | }; 18 | } 19 | 20 | auto waybar::modules::CpuFrequency::update() -> void { 21 | // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both 22 | auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); 23 | if (tooltipEnabled()) { 24 | auto tooltip = 25 | fmt::format("Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n", 26 | min_frequency, avg_frequency, max_frequency); 27 | label_.set_tooltip_text(tooltip); 28 | } 29 | auto format = format_; 30 | auto state = getState(avg_frequency); 31 | if (!state.empty() && config_["format-" + state].isString()) { 32 | format = config_["format-" + state].asString(); 33 | } 34 | 35 | if (format.empty()) { 36 | event_box_.hide(); 37 | } else { 38 | event_box_.show(); 39 | auto icons = std::vector{state}; 40 | fmt::dynamic_format_arg_store store; 41 | store.push_back(fmt::arg("icon", getIcon(avg_frequency, icons))); 42 | store.push_back(fmt::arg("max_frequency", max_frequency)); 43 | store.push_back(fmt::arg("min_frequency", min_frequency)); 44 | store.push_back(fmt::arg("avg_frequency", avg_frequency)); 45 | label_.set_markup(fmt::vformat(format, store)); 46 | } 47 | 48 | // Call parent update 49 | ALabel::update(); 50 | } 51 | 52 | std::tuple waybar::modules::CpuFrequency::getCpuFrequency() { 53 | std::vector frequencies = CpuFrequency::parseCpuFrequencies(); 54 | if (frequencies.empty()) { 55 | return {0.f, 0.f, 0.f}; 56 | } 57 | auto [min, max] = std::minmax_element(std::begin(frequencies), std::end(frequencies)); 58 | float avg_frequency = 59 | std::accumulate(std::begin(frequencies), std::end(frequencies), 0.0) / frequencies.size(); 60 | 61 | // Round frequencies with double decimal precision to get GHz 62 | float max_frequency = std::ceil(*max / 10.0) / 100.0; 63 | float min_frequency = std::ceil(*min / 10.0) / 100.0; 64 | avg_frequency = std::ceil(avg_frequency / 10.0) / 100.0; 65 | 66 | return {max_frequency, min_frequency, avg_frequency}; 67 | } 68 | --------------------------------------------------------------------------------