pax_global_header00006660000000000000000000000064136016367550014526gustar00rootroot0000000000000052 comment=35d806b8017bcd3d2059d36175660bf92a88df15 waybar-0.9.0/000077500000000000000000000000001360163675500130215ustar00rootroot00000000000000waybar-0.9.0/.clang-format000066400000000000000000000001511360163675500153710ustar00rootroot00000000000000--- BasedOnStyle: Google AlignConsecutiveDeclarations: true BinPackArguments: false ColumnLimit: 100 ... waybar-0.9.0/.editorconfig000066400000000000000000000004701360163675500154770ustar00rootroot00000000000000# EditorConfig configuration for Waybar # http://EditorConfig.org # Top-most EditorConfig file root = true [*] end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 [*.{build,css}] indent_style = space indent_size = 4 [*.{hpp,cpp}] indent_style = space indent_size = 2 waybar-0.9.0/.github/000077500000000000000000000000001360163675500143615ustar00rootroot00000000000000waybar-0.9.0/.github/FUNDING.yml000066400000000000000000000001241360163675500161730ustar00rootroot00000000000000# These are supported funding model platforms custom: https://paypal.me/ARouillard waybar-0.9.0/.gitignore000066400000000000000000000005601360163675500150120ustar00rootroot00000000000000.DS_Store *~ vgcore.* /.vscode *.swp packagecache /subprojects/**/ /build* /dist /meson.egg-info # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app waybar-0.9.0/.gitmodules000066400000000000000000000001521360163675500151740ustar00rootroot00000000000000[submodule "package/archlinux"] path = package/archlinux url = https://aur.archlinux.org/waybar-git.git waybar-0.9.0/.travis.yml000066400000000000000000000010331360163675500151270ustar00rootroot00000000000000sudo: false services: - docker git: submodules: false env: - distro: debian - distro: archlinux - distro: fedora - distro: alpine before_install: - docker pull alexays/waybar:${distro} - find . -type f \( -name '*.cpp' -o -name '*.h' \) -print0 | xargs -r0 clang-format -i script: - echo FROM alexays/waybar:${distro} > Dockerfile - echo ADD . /root >> Dockerfile - docker build -t waybar . - docker run waybar /bin/sh -c "cd /root && meson build -Dman-pages=enabled && ninja -C build" waybar-0.9.0/Dockerfiles/000077500000000000000000000000001360163675500152535ustar00rootroot00000000000000waybar-0.9.0/Dockerfiles/alpine000066400000000000000000000003701360163675500164460ustar00rootroot00000000000000# vim: ft=Dockerfile FROM alpine:latest RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev libnl3-dev pulseaudio-dev libmpdclient-dev scdoc waybar-0.9.0/Dockerfiles/archlinux000066400000000000000000000003231360163675500171710ustar00rootroot00000000000000# vim: ft=Dockerfile FROM archlinux/base:latest RUN pacman -Syu --noconfirm && \ pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp scdoc --noconfirm waybar-0.9.0/Dockerfiles/debian000066400000000000000000000005761360163675500164300ustar00rootroot00000000000000# vim: ft=Dockerfile FROM debian:sid RUN apt-get update && \ apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc && \ apt-get clean waybar-0.9.0/Dockerfiles/fedora000066400000000000000000000005621360163675500164410ustar00rootroot00000000000000# vim: ft=Dockerfile FROM fedora:30 RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel scdoc -y && \ dnf group install "C Development Tools and Libraries" -y && \ dnf clean all -y waybar-0.9.0/Dockerfiles/opensuse000066400000000000000000000006251360163675500170420ustar00rootroot00000000000000# vim: ft=Dockerfile FROM opensuse/tumbleweed:latest RUN zypper -n up && \ zypper -n install -t pattern devel_C_C++ && \ zypper -n install git meson clang libinput10 libinput-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc waybar-0.9.0/LICENSE000066400000000000000000000020451360163675500140270ustar00rootroot00000000000000MIT License Copyright (c) 2018 Alex Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. waybar-0.9.0/Makefile000066400000000000000000000003761360163675500144670ustar00rootroot00000000000000.PHONY: build build-debug run clean default install default: run build: meson build ninja -C build build-debug: meson build --buildtype=debug ninja -C build install: build ninja -C build install run: build ./build/waybar clean: rm -rf build waybar-0.9.0/README.md000066400000000000000000000042431360163675500143030ustar00rootroot00000000000000# Waybar [![Travis](https://travis-ci.org/Alexays/Waybar.svg?branch=master)](https://travis-ci.org/Alexays/Waybar) [![Licence](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Paypal Donate](https://img.shields.io/badge/Donate-Paypal-2244dd.svg)](https://paypal.me/ARouillard)
![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png) > Highly customizable Wayland bar for Sway and Wlroots based compositors.
> Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or [AUR](https://aur.archlinux.org/packages/waybar-git/), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar)
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)* **Current features** - Sway (Workspaces, Binding mode, Focused window name) - Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Local time - Battery - Network - Pulseaudio - Disk - Memory - Cpu load average - Temperature - MPD - Custom scripts - Multiple output configuration - And much more customizations **Configuration and Styling** [See the wiki for more details](https://github.com/Alexays/Waybar/wiki). **How to build** ```bash $ git clone https://github.com/Alexays/Waybar $ cd Waybar $ meson build $ ninja -C build $ ./build/waybar # If you want to install it $ ninja -C build install $ waybar ``` **Dependencies** ``` gtkmm3 jsoncpp libinput libsigc++ fmt wayland wlroots libpulse [Pulseaudio module] libnl [Network module] sway [Sway modules] libdbusmenu-gtk3 [Tray module] libmpdclient [MPD module] ``` On Ubuntu 19.10 you can install all the relevant dependencies using this command: ``` sudo apt install libgtkmm-3.0-dev libjsoncpp-dev libinput-dev libsigc++-2.0-dev libpulse-dev libnl-3-dev libdbusmenu-gtk3-dev libnl-genl-3-dev libfmt-dev clang-tidy scdoc libmpdclient-dev ``` Contributions welcome! - have fun :)
The style guidelines is [Google's](https://google.github.io/styleguide/cppguide.html) ## License Waybar is licensed under the MIT license. [See LICENSE for more information](https://github.com/Alexays/Waybar/blob/master/LICENSE). waybar-0.9.0/include/000077500000000000000000000000001360163675500144445ustar00rootroot00000000000000waybar-0.9.0/include/ALabel.hpp000066400000000000000000000016121360163675500162750ustar00rootroot00000000000000#pragma once #include #include #include #include "AModule.hpp" namespace waybar { class ALabel : public AModule { public: ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format, uint16_t interval = 0, bool ellipsize = false); virtual ~ALabel() = default; virtual auto update() -> void; virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0); protected: Gtk::Label label_; std::string format_; std::string click_param; const std::chrono::seconds interval_; bool alt_ = false; std::string default_format_; virtual bool handleToggle(GdkEventButton *const &e); virtual std::string getState(uint8_t value, bool lesser = false); }; } // namespace waybar waybar-0.9.0/include/AModule.hpp000066400000000000000000000016331360163675500165060ustar00rootroot00000000000000#pragma once #include #include #include #include #include "IModule.hpp" namespace waybar { class AModule : public IModule { public: AModule(const Json::Value &, const std::string &, const std::string &, bool enable_click = false, bool enable_scroll = false); virtual ~AModule(); virtual auto update() -> void; virtual operator Gtk::Widget &(); Glib::Dispatcher dp; protected: enum SCROLL_DIR { NONE, UP, DOWN, LEFT, RIGHT }; SCROLL_DIR getScrollDir(GdkEventScroll *e); bool tooltipEnabled(); const Json::Value &config_; Gtk::EventBox event_box_; virtual bool handleToggle(GdkEventButton *const &ev); virtual bool handleScroll(GdkEventScroll *); private: std::vector pid_; gdouble distance_scrolled_y_; gdouble distance_scrolled_x_; }; } // namespace waybar waybar-0.9.0/include/IModule.hpp000066400000000000000000000003531360163675500165140ustar00rootroot00000000000000#pragma once #include namespace waybar { class IModule { public: virtual ~IModule() = default; virtual auto update() -> void = 0; virtual operator Gtk::Widget &() = 0; }; } // namespace waybar waybar-0.9.0/include/bar.hpp000066400000000000000000000061631360163675500157270ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include "AModule.hpp" #include "idle-inhibit-unstable-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" namespace waybar { class Factory; struct waybar_output { Glib::RefPtr monitor; std::string name; std::unique_ptr xdg_output = { nullptr, &zxdg_output_v1_destroy}; }; class Bar { public: Bar(struct waybar_output *w_output, const Json::Value &); Bar(const Bar &) = delete; ~Bar() = default; auto toggle() -> void; void handleSignal(int); struct waybar_output *output; Json::Value config; Gtk::Window window; struct wl_surface * surface; bool visible = true; bool vertical = false; private: static constexpr const char *MIN_HEIGHT_MSG = "Requested height: {} exceeds the minimum height: {} required by the modules"; static constexpr const char *MIN_WIDTH_MSG = "Requested width: {} exceeds the minimum width: {} required by the modules"; static constexpr const char *BAR_SIZE_MSG = "Bar configured (width: {}, height: {}) for output: {}"; static constexpr const char *SIZE_DEFINED = "{} size is defined in the config file so it will stay like that"; static void layerSurfaceHandleConfigure(void *, struct zwlr_layer_surface_v1 *, uint32_t, uint32_t, uint32_t); static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *); #ifdef HAVE_GTK_LAYER_SHELL void initGtkLayerShell(); #endif void onConfigure(GdkEventConfigure *ev); void onRealize(); void onMap(GdkEventAny *ev); void setExclusiveZone(uint32_t width, uint32_t height); void setSurfaceSize(uint32_t width, uint32_t height); auto setupWidgets() -> void; void getModules(const Factory &, const std::string &); void setupAltFormatKeyForModule(const std::string &module_name); void setupAltFormatKeyForModuleList(const char *module_list_name); struct margins { int top = 0; int right = 0; int bottom = 0; int left = 0; } margins_; struct zwlr_layer_surface_v1 *layer_surface_; // use gtk-layer-shell instead of handling layer surfaces directly bool use_gls_ = false; uint32_t width_ = 0; uint32_t height_ = 1; uint8_t anchor_; Gtk::Box left_; Gtk::Box center_; Gtk::Box right_; Gtk::Box box_; std::vector> modules_left_; std::vector> modules_center_; std::vector> modules_right_; }; } // namespace waybar waybar-0.9.0/include/client.hpp000066400000000000000000000044141360163675500164360ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "bar.hpp" namespace waybar { class Client { public: static Client *inst(); int main(int argc, char *argv[]); Glib::RefPtr gtk_app; Glib::RefPtr gdk_display; struct wl_display * wl_display = nullptr; struct wl_registry * registry = nullptr; struct zwlr_layer_shell_v1 * layer_shell = nullptr; struct zxdg_output_manager_v1 * xdg_output_manager = nullptr; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr; std::vector> bars; private: Client() = default; std::tuple getConfigs(const std::string &config, const std::string &style) const; void bindInterfaces(); const std::string getValidPath(const std::vector &paths) const; void handleOutput(std::unique_ptr &output); bool isValidOutput(const Json::Value &config, std::unique_ptr &output); auto setupConfig(const std::string &config_file) -> void; auto setupCss(const std::string &css_file) -> void; std::unique_ptr &getOutput(void *); std::vector getOutputConfigs(std::unique_ptr &output); static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name); static void handleOutputName(void *, struct zxdg_output_v1 *, const char *); void handleMonitorAdded(Glib::RefPtr monitor); void handleMonitorRemoved(Glib::RefPtr monitor); Json::Value config_; Glib::RefPtr style_context_; Glib::RefPtr css_provider_; std::vector> outputs_; }; } // namespace waybar waybar-0.9.0/include/factory.hpp000066400000000000000000000020231360163675500166210ustar00rootroot00000000000000#pragma once #include #include "modules/clock.hpp" #ifdef HAVE_SWAY #include "modules/sway/mode.hpp" #include "modules/sway/window.hpp" #include "modules/sway/workspaces.hpp" #endif #ifndef NO_FILESYSTEM #include "modules/battery.hpp" #endif #include "modules/cpu.hpp" #include "modules/idle_inhibitor.hpp" #include "modules/memory.hpp" #include "modules/disk.hpp" #if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM) #include "modules/sni/tray.hpp" #endif #ifdef HAVE_LIBNL #include "modules/network.hpp" #endif #ifdef HAVE_LIBUDEV #include "modules/backlight.hpp" #endif #ifdef HAVE_LIBPULSE #include "modules/pulseaudio.hpp" #endif #ifdef HAVE_LIBMPDCLIENT #include "modules/mpd.hpp" #endif #include "bar.hpp" #include "modules/custom.hpp" #include "modules/temperature.hpp" namespace waybar { class Factory { public: Factory(const Bar& bar, const Json::Value& config); AModule* makeModule(const std::string& name) const; private: const Bar& bar_; const Json::Value& config_; }; } // namespace waybar waybar-0.9.0/include/modules/000077500000000000000000000000001360163675500161145ustar00rootroot00000000000000waybar-0.9.0/include/modules/backlight.hpp000066400000000000000000000034021360163675500205540ustar00rootroot00000000000000#pragma once #include #include #include #include #include "ALabel.hpp" #include "util/json.hpp" #include "util/sleeper_thread.hpp" struct udev; struct udev_device; namespace waybar::modules { class Backlight : public ALabel { class BacklightDev { public: BacklightDev() = default; BacklightDev(std::string name, int actual, int max); std::string_view name() const; int get_actual() const; void set_actual(int actual); int get_max() const; void set_max(int max); friend inline bool operator==(const BacklightDev &lhs, const BacklightDev &rhs) { return lhs.name_ == rhs.name_ && lhs.actual_ == rhs.actual_ && lhs.max_ == rhs.max_; } private: std::string name_; int actual_ = 1; int max_ = 1; }; public: Backlight(const std::string &, const Json::Value &); ~Backlight(); auto update() -> void; private: template static const BacklightDev *best_device(ForwardIt first, ForwardIt last, std::string_view); template static void upsert_device(ForwardIt first, ForwardIt last, Inserter inserter, udev_device *dev); template static void enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, udev *udev); const std::string preferred_device_; static constexpr int EPOLL_MAX_EVENTS = 16; std::optional previous_best_; std::string previous_format_; std::mutex udev_thread_mutex_; std::vector devices_; // thread must destruct before shared data util::SleeperThread udev_thread_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/battery.hpp000066400000000000000000000024351360163675500203030ustar00rootroot00000000000000#pragma once #ifdef FILESYSTEM_EXPERIMENTAL #include #else #include #endif #include #include #include #include #include #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { #ifdef FILESYSTEM_EXPERIMENTAL namespace fs = std::experimental::filesystem; #else namespace fs = std::filesystem; #endif class Battery : public ALabel { public: Battery(const std::string&, const Json::Value&); ~Battery(); auto update() -> void; private: static inline const fs::path data_dir_ = "/sys/class/power_supply/"; void getBatteries(); void worker(); const std::string getAdapterStatus(uint8_t capacity) const; const std::tuple getInfos() const; const std::string formatTimeRemaining(float hoursRemaining); std::vector batteries_; fs::path adapter_; int fd_; std::vector wds_; std::string old_status_; util::SleeperThread thread_; util::SleeperThread thread_timer_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/clock.hpp000066400000000000000000000006451360163675500177250ustar00rootroot00000000000000#pragma once #include #if FMT_VERSION < 60000 #include #else #include #endif #include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { class Clock : public ALabel { public: Clock(const std::string&, const Json::Value&); ~Clock() = default; auto update() -> void; private: util::SleeperThread thread_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/cpu.hpp000066400000000000000000000013751360163675500174220ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { class Cpu : public ALabel { public: Cpu(const std::string&, const Json::Value&); ~Cpu() = default; auto update() -> void; private: static inline const std::string data_dir_ = "/proc/stat"; uint16_t getCpuLoad(); std::tuple getCpuUsage(); std::vector> parseCpuinfo(); std::vector> prev_times_; util::SleeperThread thread_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/custom.hpp000066400000000000000000000017641360163675500201470ustar00rootroot00000000000000#pragma once #include #include #include #include "ALabel.hpp" #include "util/command.hpp" #include "util/json.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { class Custom : public ALabel { public: Custom(const std::string&, const std::string&, const Json::Value&); ~Custom(); auto update() -> void; void refresh(int /*signal*/); private: void delayWorker(); void continuousWorker(); void parseOutputRaw(); void parseOutputJson(); bool handleScroll(GdkEventScroll* e); bool handleToggle(GdkEventButton* const& e); const std::string name_; std::string text_; std::string alt_; std::string tooltip_; std::vector class_; int percentage_; FILE* fp_; int pid_; util::command::res output_; util::JsonParser parser_; util::SleeperThread thread_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/disk.hpp000066400000000000000000000006531360163675500175630ustar00rootroot00000000000000#pragma once #include #include #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" #include "util/format.hpp" namespace waybar::modules { class Disk : public ALabel { public: Disk(const std::string&, const Json::Value&); ~Disk() = default; auto update() -> void; private: util::SleeperThread thread_; std::string path_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/idle_inhibitor.hpp000066400000000000000000000010611360163675500216070ustar00rootroot00000000000000#pragma once #include #include "ALabel.hpp" #include "bar.hpp" #include "client.hpp" namespace waybar::modules { class IdleInhibitor : public ALabel { public: IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&); ~IdleInhibitor(); auto update() -> void; private: bool handleToggle(GdkEventButton* const& e); const Bar& bar_; std::string status_; struct zwp_idle_inhibitor_v1* idle_inhibitor_; int pid_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/memory.hpp000066400000000000000000000010201360163675500201260ustar00rootroot00000000000000#pragma once #include #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { class Memory : public ALabel { public: Memory(const std::string&, const Json::Value&); ~Memory() = default; auto update() -> void; private: static inline const std::string data_dir_ = "/proc/meminfo"; void parseMeminfo(); unsigned long memtotal_; unsigned long memfree_; util::SleeperThread thread_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/mpd.hpp000066400000000000000000000042141360163675500174060ustar00rootroot00000000000000#pragma once #include #include #include #include #include "ALabel.hpp" namespace waybar::modules { class MPD : public ALabel { public: MPD(const std::string&, const Json::Value&); auto update() -> void; private: std::thread periodic_updater(); std::string getTag(mpd_tag_type type, unsigned idx = 0); void setLabel(); std::string getStateIcon(); std::string getOptionIcon(std::string optionName, bool activated); std::thread event_listener(); // Assumes `connection_lock_` is locked void tryConnect(); // If checking errors on the main connection, make sure to lock it using // `connection_lock_` before calling checkErrors void checkErrors(mpd_connection* conn); // Assumes `connection_lock_` is locked void fetchState(); void waitForEvent(); bool handlePlayPause(GdkEventButton* const&); bool stopped(); bool playing(); const std::string module_name_; using unique_connection = std::unique_ptr; using unique_status = std::unique_ptr; using unique_song = std::unique_ptr; // Not using unique_ptr since we don't manage the pointer // (It's either nullptr, or from the config) const char* server_; const unsigned port_; unsigned timeout_; // We need a mutex here because we can trigger updates from multiple thread: // the event based updates, the periodic updates needed for the elapsed time, // and the click play/pause feature std::mutex connection_lock_; unique_connection connection_; // The alternate connection will be used to wait for events: since it will // be blocking (idle) we can't send commands via this connection // // No lock since only used in the event listener thread unique_connection alternate_connection_; // Protect them using the `connection_lock_` unique_status status_; mpd_state state_; unique_song song_; // To make sure the previous periodic_updater stops before creating a new one std::mutex periodic_lock_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/network.hpp000066400000000000000000000045221360163675500203210ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { class Network : public ALabel { public: Network(const std::string&, const Json::Value&); ~Network(); auto update() -> void; private: static const uint8_t MAX_RETRY = 5; static const uint8_t EPOLL_MAX = 200; static int handleEvents(struct nl_msg*, void*); static int handleScan(struct nl_msg*, void*); void worker(); void createInfoSocket(); void createEventSocket(); int getExternalInterface(int skip_idx = -1) const; void getInterfaceAddress(); int netlinkRequest(void*, uint32_t, uint32_t groups = 0) const; int netlinkResponse(void*, uint32_t, uint32_t groups = 0) const; void parseEssid(struct nlattr**); void parseSignal(struct nlattr**); void parseFreq(struct nlattr**); bool associatedOrJoined(struct nlattr**); bool checkInterface(struct ifinfomsg* rtif, std::string name); int getPreferredIface(int skip_idx = -1, bool wait = true) const; auto getInfo() -> void; void checkNewInterface(struct ifinfomsg* rtif); const std::string getNetworkState() const; void clearIface(); bool wildcardMatch(const std::string& pattern, const std::string& text) const; int ifid_; sa_family_t family_; struct sockaddr_nl nladdr_ = {0}; struct nl_sock* sock_ = nullptr; struct nl_sock* ev_sock_ = nullptr; int efd_; int ev_fd_; int nl80211_id_; std::mutex mutex_; unsigned long long bandwidth_down_total_; unsigned long long bandwidth_up_total_; std::string state_; std::string essid_; std::string ifname_; std::string ipaddr_; std::string netmask_; int cidr_; int32_t signal_strength_dbm_; uint8_t signal_strength_; uint32_t frequency_; util::SleeperThread thread_; util::SleeperThread thread_timer_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/pulseaudio.hpp000066400000000000000000000024461360163675500210050ustar00rootroot00000000000000#pragma once #include #include #include #include #include "ALabel.hpp" namespace waybar::modules { class Pulseaudio : public ALabel { public: Pulseaudio(const std::string&, const Json::Value&); ~Pulseaudio(); auto update() -> void; private: static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*); static void contextStateCb(pa_context*, void*); static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*); static void sourceInfoCb(pa_context*, const pa_source_info* i, int, void* data); static void serverInfoCb(pa_context*, const pa_server_info*, void*); static void volumeModifyCb(pa_context*, int, void*); bool handleScroll(GdkEventScroll* e); const std::string getPortIcon() const; pa_threaded_mainloop* mainloop_; pa_mainloop_api* mainloop_api_; pa_context* context_; // SINK uint32_t sink_idx_{0}; uint16_t volume_; pa_cvolume pa_volume_; bool muted_; std::string port_name_; std::string desc_; std::string monitor_; // SOURCE uint32_t source_idx_{0}; uint16_t source_volume_; bool source_muted_; std::string source_port_name_; std::string source_desc_; }; } // namespace waybar::modules waybar-0.9.0/include/modules/sni/000077500000000000000000000000001360163675500167055ustar00rootroot00000000000000waybar-0.9.0/include/modules/sni/host.hpp000066400000000000000000000034601360163675500203760ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "modules/sni/item.hpp" namespace waybar::modules::SNI { class Host { public: Host(const std::size_t id, const Json::Value&, const std::function&)>&, const std::function&)>&); ~Host(); private: void busAcquired(const Glib::RefPtr&, Glib::ustring); void nameAppeared(const Glib::RefPtr&, Glib::ustring, const Glib::ustring&); void nameVanished(const Glib::RefPtr&, Glib::ustring); static void proxyReady(GObject*, GAsyncResult*, gpointer); static void registerHost(GObject*, GAsyncResult*, gpointer); static void itemRegistered(SnWatcher*, const gchar*, gpointer); static void itemUnregistered(SnWatcher*, const gchar*, gpointer); std::tuple getBusNameAndObjectPath(const std::string); void addRegisteredItem(std::string service); std::vector> items_; const std::string bus_name_; const std::string object_path_; std::size_t bus_name_id_; std::size_t watcher_id_; GCancellable* cancellable_ = nullptr; SnWatcher* watcher_ = nullptr; const Json::Value& config_; const std::function&)> on_add_; const std::function&)> on_remove_; }; } // namespace waybar::modules::SNI waybar-0.9.0/include/modules/sni/item.hpp000066400000000000000000000050771360163675500203650ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #ifdef FILESYSTEM_EXPERIMENTAL #include #else #include #endif namespace waybar::modules::SNI { class Item : public sigc::trackable { public: Item(const std::string&, const std::string&, const Json::Value&); ~Item() = default; std::string bus_name; std::string object_path; int icon_size; int effective_icon_size; Gtk::Image image; Gtk::EventBox event_box; std::string category; std::string id; std::string status; std::string title; int32_t window_id; std::string icon_name; Glib::RefPtr icon_pixmap; Glib::RefPtr icon_theme; std::string overlay_icon_name; std::string attention_icon_name; std::string attention_movie_name; std::string icon_theme_path; std::string menu; DbusmenuGtkMenu* dbus_menu = nullptr; Gtk::Menu* gtk_menu = nullptr; /** * ItemIsMenu flag means that the item only supports the context menu. * Default value is true because libappindicator supports neither ItemIsMenu nor Activate method * while compliant SNI implementation would always reset the flag to desired value. */ bool item_is_menu = true; private: void proxyReady(Glib::RefPtr& result); void setProperty(const Glib::ustring& name, Glib::VariantBase& value); void getUpdatedProperties(); void processUpdatedProperties(Glib::RefPtr& result); void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, const Glib::VariantContainerBase& arguments); void updateImage(); Glib::RefPtr extractPixBuf(GVariant* variant); Glib::RefPtr getIconByName(const std::string& name, int size); static void onMenuDestroyed(Item* self, GObject* old_menu_pointer); void makeMenu(); bool handleClick(GdkEventButton* const& /*ev*/); Glib::RefPtr proxy_; Glib::RefPtr cancellable_; bool update_pending_; }; } // namespace waybar::modules::SNI waybar-0.9.0/include/modules/sni/tray.hpp000066400000000000000000000012041360163675500203720ustar00rootroot00000000000000#pragma once #include #include "AModule.hpp" #include "bar.hpp" #include "modules/sni/host.hpp" #include "modules/sni/watcher.hpp" #include "util/json.hpp" namespace waybar::modules::SNI { class Tray : public AModule { public: Tray(const std::string&, const Bar&, const Json::Value&); ~Tray() = default; auto update() -> void; private: void onAdd(std::unique_ptr& item); void onRemove(std::unique_ptr& item); static inline std::size_t nb_hosts_ = 0; Gtk::Box box_; SNI::Watcher watcher_; SNI::Host host_; }; } // namespace waybar::modules::SNI waybar-0.9.0/include/modules/sni/watcher.hpp000066400000000000000000000024371360163675500210610ustar00rootroot00000000000000#pragma once #include #include #include namespace waybar::modules::SNI { class Watcher { public: Watcher(std::size_t id); ~Watcher(); private: typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType; typedef struct { GfWatchType type; Watcher * watcher; gchar * service; gchar * bus_name; gchar * object_path; guint watch_id; } GfWatch; void busAcquired(const Glib::RefPtr &, Glib::ustring); static gboolean handleRegisterHost(Watcher *, GDBusMethodInvocation *, const gchar *); static gboolean handleRegisterItem(Watcher *, GDBusMethodInvocation *, const gchar *); static GfWatch *gfWatchFind(GSList *list, const gchar *bus_name, const gchar *object_path); static GfWatch *gfWatchNew(GfWatchType, const gchar *, const gchar *, const gchar *, Watcher *); static void nameVanished(GDBusConnection *connection, const char *name, gpointer data); static void gfWatchFree(gpointer data); void updateRegisteredItems(SnWatcher *obj); uint32_t bus_name_id_; uint32_t watcher_id_; GSList * hosts_ = nullptr; GSList * items_ = nullptr; SnWatcher *watcher_ = nullptr; }; } // namespace waybar::modules::SNI waybar-0.9.0/include/modules/sway/000077500000000000000000000000001360163675500170775ustar00rootroot00000000000000waybar-0.9.0/include/modules/sway/ipc/000077500000000000000000000000001360163675500176525ustar00rootroot00000000000000waybar-0.9.0/include/modules/sway/ipc/client.hpp000066400000000000000000000020761360163675500216460ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include "ipc.hpp" namespace waybar::modules::sway { class Ipc { public: Ipc(); ~Ipc(); struct ipc_response { uint32_t size; uint32_t type; std::string payload; }; sigc::signal signal_event; sigc::signal signal_cmd; void sendCmd(uint32_t type, const std::string &payload = ""); void subscribe(const std::string &payload); void handleEvent(); protected: static inline const std::string ipc_magic_ = "i3-ipc"; static inline const size_t ipc_header_size_ = ipc_magic_.size() + 8; const std::string getSocketPath() const; int open(const std::string &) const; struct ipc_response send(int fd, uint32_t type, const std::string &payload = ""); struct ipc_response recv(int fd); int fd_; int fd_event_; std::mutex mutex_; }; } // namespace waybar::modules::sway waybar-0.9.0/include/modules/sway/ipc/ipc.hpp000066400000000000000000000015411360163675500211370ustar00rootroot00000000000000#pragma once #define event_mask(ev) (1u << (ev & 0x7F)) enum ipc_command_type { // i3 command types - see i3's I3_REPLY_TYPE constants IPC_COMMAND = 0, IPC_GET_WORKSPACES = 1, IPC_SUBSCRIBE = 2, IPC_GET_OUTPUTS = 3, IPC_GET_TREE = 4, IPC_GET_MARKS = 5, IPC_GET_BAR_CONFIG = 6, IPC_GET_VERSION = 7, IPC_GET_BINDING_MODES = 8, IPC_GET_CONFIG = 9, IPC_SEND_TICK = 10, // sway-specific command types IPC_GET_INPUTS = 100, IPC_GET_SEATS = 101, // Events sent from sway to clients. Events have the highest bits set. IPC_EVENT_WORKSPACE = ((1 << 31) | 0), IPC_EVENT_OUTPUT = ((1 << 31) | 1), IPC_EVENT_MODE = ((1 << 31) | 2), IPC_EVENT_WINDOW = ((1 << 31) | 3), IPC_EVENT_BARCONFIG_UPDATE = ((1 << 31) | 4), IPC_EVENT_BINDING = ((1 << 31) | 5), IPC_EVENT_SHUTDOWN = ((1 << 31) | 6), IPC_EVENT_TICK = ((1 << 31) | 7), }; waybar-0.9.0/include/modules/sway/mode.hpp000066400000000000000000000012151360163675500205330ustar00rootroot00000000000000#pragma once #include #include "ALabel.hpp" #include "bar.hpp" #include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules::sway { class Mode : public ALabel, public sigc::trackable { public: Mode(const std::string&, const Json::Value&); ~Mode() = default; auto update() -> void; private: void onEvent(const struct Ipc::ipc_response&); void worker(); std::string mode_; util::JsonParser parser_; std::mutex mutex_; util::SleeperThread thread_; Ipc ipc_; }; } // namespace waybar::modules::sway waybar-0.9.0/include/modules/sway/window.hpp000066400000000000000000000024301360163675500211160ustar00rootroot00000000000000#pragma once #include #include #include "ALabel.hpp" #include "bar.hpp" #include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules::sway { class Window : public ALabel, public sigc::trackable { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); ~Window() = default; auto update() -> void; private: void onEvent(const struct Ipc::ipc_response&); void onCmd(const struct Ipc::ipc_response&); void worker(); std::tuple getFocusedNode(const Json::Value& nodes, std::string& output); void getTree(); const Bar& bar_; std::string window_; int windowId_; std::string app_id_; std::string old_app_id_; std::size_t app_nb_; util::JsonParser parser_; std::mutex mutex_; util::SleeperThread thread_; Ipc ipc_; }; } // namespace waybar::modules::sway waybar-0.9.0/include/modules/sway/workspaces.hpp000066400000000000000000000031501360163675500217700ustar00rootroot00000000000000#pragma once #include #include #include #include "AModule.hpp" #include "bar.hpp" #include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules::sway { class Workspaces : public AModule, public sigc::trackable { public: Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); ~Workspaces() = default; auto update() -> void; private: void onCmd(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&); void worker(); bool filterButtons(); Gtk::Button& addButton(const Json::Value&); void onButtonReady(const Json::Value&, Gtk::Button&); std::string getIcon(const std::string&, const Json::Value&); const std::string getCycleWorkspace(std::vector::iterator, bool prev) const; uint16_t getWorkspaceIndex(const std::string& name) const; std::string trimWorkspaceName(std::string); bool handleScroll(GdkEventScroll*); const Bar& bar_; std::vector workspaces_; std::vector workspaces_order_; Gtk::Box box_; util::JsonParser parser_; std::unordered_map buttons_; std::mutex mutex_; util::SleeperThread thread_; Ipc ipc_; }; } // namespace waybar::modules::sway waybar-0.9.0/include/modules/temperature.hpp000066400000000000000000000010041360163675500211550ustar00rootroot00000000000000#pragma once #include #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { class Temperature : public ALabel { public: Temperature(const std::string&, const Json::Value&); ~Temperature() = default; auto update() -> void; private: std::tuple getTemperature(); bool isCritical(uint16_t); std::string file_path_; util::SleeperThread thread_; }; } // namespace waybar::modules waybar-0.9.0/include/util/000077500000000000000000000000001360163675500154215ustar00rootroot00000000000000waybar-0.9.0/include/util/clara.hpp000066400000000000000000001246731360163675500172310ustar00rootroot00000000000000// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // See https://github.com/philsquared/Clara for more details // Clara v1.1.5 #ifndef CLARA_HPP_INCLUDED #define CLARA_HPP_INCLUDED #ifndef CLARA_CONFIG_CONSOLE_WIDTH #define CLARA_CONFIG_CONSOLE_WIDTH 80 #endif #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH #endif #ifndef CLARA_CONFIG_OPTIONAL_TYPE #ifdef __has_include #if __has_include() && __cplusplus >= 201703L #include #define CLARA_CONFIG_OPTIONAL_TYPE std::optional #endif #endif #endif // ----------- #included from clara_textflow.hpp ----------- // TextFlowCpp // // A single-header library for wrapping and laying out basic text, by Phil Nash // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // This project is hosted at https://github.com/philsquared/textflowcpp #ifndef CLARA_TEXTFLOW_HPP_INCLUDED #define CLARA_TEXTFLOW_HPP_INCLUDED #include #include #include #include #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 #endif namespace clara { namespace TextFlow { inline auto isWhitespace( char c ) -> bool { static std::string chars = " \t\n\r"; return chars.find( c ) != std::string::npos; } inline auto isBreakableBefore( char c ) -> bool { static std::string chars = "[({<|"; return chars.find( c ) != std::string::npos; } inline auto isBreakableAfter( char c ) -> bool { static std::string chars = "])}>.,:;*+-=&/\\"; return chars.find( c ) != std::string::npos; } class Columns; class Column { std::vector m_strings; size_t m_width = CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; size_t m_indent = 0; size_t m_initialIndent = std::string::npos; public: class iterator { friend Column; Column const& m_column; size_t m_stringIndex = 0; size_t m_pos = 0; size_t m_len = 0; size_t m_end = 0; bool m_suffix = false; iterator( Column const& column, size_t stringIndex ) : m_column( column ), m_stringIndex( stringIndex ) {} auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } auto isBoundary( size_t at ) const -> bool { assert( at > 0 ); assert( at <= line().size() ); return at == line().size() || ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || isBreakableBefore( line()[at] ) || isBreakableAfter( line()[at-1] ); } void calcLength() { assert( m_stringIndex < m_column.m_strings.size() ); m_suffix = false; auto width = m_column.m_width-indent(); m_end = m_pos; while( m_end < line().size() && line()[m_end] != '\n' ) ++m_end; if( m_end < m_pos + width ) { m_len = m_end - m_pos; } else { size_t len = width; while (len > 0 && !isBoundary(m_pos + len)) --len; while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) --len; if (len > 0) { m_len = len; } else { m_suffix = true; m_len = width - 1; } } } auto indent() const -> size_t { auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; return initial == std::string::npos ? m_column.m_indent : initial; } auto addIndentAndSuffix(std::string const &plain) const -> std::string { return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); } public: using difference_type = std::ptrdiff_t; using value_type = std::string; using pointer = value_type*; using reference = value_type&; using iterator_category = std::forward_iterator_tag; explicit iterator( Column const& column ) : m_column( column ) { assert( m_column.m_width > m_column.m_indent ); assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); calcLength(); if( m_len == 0 ) m_stringIndex++; // Empty string } auto operator *() const -> std::string { assert( m_stringIndex < m_column.m_strings.size() ); assert( m_pos <= m_end ); return addIndentAndSuffix(line().substr(m_pos, m_len)); } auto operator ++() -> iterator& { m_pos += m_len; if( m_pos < line().size() && line()[m_pos] == '\n' ) m_pos += 1; else while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) ++m_pos; if( m_pos == line().size() ) { m_pos = 0; ++m_stringIndex; } if( m_stringIndex < m_column.m_strings.size() ) calcLength(); return *this; } auto operator ++(int) -> iterator { iterator prev( *this ); operator++(); return prev; } auto operator ==( iterator const& other ) const -> bool { return m_pos == other.m_pos && m_stringIndex == other.m_stringIndex && &m_column == &other.m_column; } auto operator !=( iterator const& other ) const -> bool { return !operator==( other ); } }; using const_iterator = iterator; explicit Column( std::string const& text ) { m_strings.push_back( text ); } auto width( size_t newWidth ) -> Column& { assert( newWidth > 0 ); m_width = newWidth; return *this; } auto indent( size_t newIndent ) -> Column& { m_indent = newIndent; return *this; } auto initialIndent( size_t newIndent ) -> Column& { m_initialIndent = newIndent; return *this; } auto width() const -> size_t { return m_width; } auto begin() const -> iterator { return iterator( *this ); } auto end() const -> iterator { return { *this, m_strings.size() }; } inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { bool first = true; for( auto line : col ) { if( first ) first = false; else os << "\n"; os << line; } return os; } auto operator + ( Column const& other ) -> Columns; auto toString() const -> std::string { std::ostringstream oss; oss << *this; return oss.str(); } }; class Spacer : public Column { public: explicit Spacer( size_t spaceWidth ) : Column( "" ) { width( spaceWidth ); } }; class Columns { std::vector m_columns; public: class iterator { friend Columns; struct EndTag {}; std::vector const& m_columns; std::vector m_iterators; size_t m_activeIterators; iterator( Columns const& columns, EndTag ) : m_columns( columns.m_columns ), m_activeIterators( 0 ) { m_iterators.reserve( m_columns.size() ); for( auto const& col : m_columns ) m_iterators.push_back( col.end() ); } public: using difference_type = std::ptrdiff_t; using value_type = std::string; using pointer = value_type*; using reference = value_type&; using iterator_category = std::forward_iterator_tag; explicit iterator( Columns const& columns ) : m_columns( columns.m_columns ), m_activeIterators( m_columns.size() ) { m_iterators.reserve( m_columns.size() ); for( auto const& col : m_columns ) m_iterators.push_back( col.begin() ); } auto operator ==( iterator const& other ) const -> bool { return m_iterators == other.m_iterators; } auto operator !=( iterator const& other ) const -> bool { return m_iterators != other.m_iterators; } auto operator *() const -> std::string { std::string row, padding; for( size_t i = 0; i < m_columns.size(); ++i ) { auto width = m_columns[i].width(); if( m_iterators[i] != m_columns[i].end() ) { std::string col = *m_iterators[i]; row += padding + col; if( col.size() < width ) padding = std::string( width - col.size(), ' ' ); else padding = ""; } else { padding += std::string( width, ' ' ); } } return row; } auto operator ++() -> iterator& { for( size_t i = 0; i < m_columns.size(); ++i ) { if (m_iterators[i] != m_columns[i].end()) ++m_iterators[i]; } return *this; } auto operator ++(int) -> iterator { iterator prev( *this ); operator++(); return prev; } }; using const_iterator = iterator; auto begin() const -> iterator { return iterator( *this ); } auto end() const -> iterator { return { *this, iterator::EndTag() }; } auto operator += ( Column const& col ) -> Columns& { m_columns.push_back( col ); return *this; } auto operator + ( Column const& col ) -> Columns { Columns combined = *this; combined += col; return combined; } inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { bool first = true; for( auto line : cols ) { if( first ) first = false; else os << "\n"; os << line; } return os; } auto toString() const -> std::string { std::ostringstream oss; oss << *this; return oss.str(); } }; inline auto Column::operator + ( Column const& other ) -> Columns { Columns cols; cols += *this; cols += other; return cols; } }} #endif // CLARA_TEXTFLOW_HPP_INCLUDED // ----------- end of #include from clara_textflow.hpp ----------- // ........... back in clara.hpp #include #include #include #if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) #define CLARA_PLATFORM_WINDOWS #endif namespace clara { namespace detail { // Traits for extracting arg and return type of lambdas (for single argument lambdas) template struct UnaryLambdaTraits : UnaryLambdaTraits {}; template struct UnaryLambdaTraits { static const bool isValid = false; }; template struct UnaryLambdaTraits { static const bool isValid = true; using ArgType = typename std::remove_const::type>::type; using ReturnType = ReturnT; }; class TokenStream; // Transport for raw args (copied from main args, or supplied via init list for testing) class Args { friend TokenStream; std::string m_exeName; std::vector m_args; public: Args( int argc, char const* const* argv ) : m_exeName(argv[0]), m_args(argv + 1, argv + argc) {} Args( std::initializer_list args ) : m_exeName( *args.begin() ), m_args( args.begin()+1, args.end() ) {} auto exeName() const -> std::string { return m_exeName; } }; // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string // may encode an option + its argument if the : or = form is used enum class TokenType { Option, Argument }; struct Token { TokenType type; std::string token; }; inline auto isOptPrefix( char c ) -> bool { return c == '-' #ifdef CLARA_PLATFORM_WINDOWS || c == '/' #endif ; } // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled class TokenStream { using Iterator = std::vector::const_iterator; Iterator it; Iterator itEnd; std::vector m_tokenBuffer; void loadBuffer() { m_tokenBuffer.resize( 0 ); // Skip any empty strings while( it != itEnd && it->empty() ) ++it; if( it != itEnd ) { auto const &next = *it; if( isOptPrefix( next[0] ) ) { auto delimiterPos = next.find_first_of( " :=" ); if( delimiterPos != std::string::npos ) { m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); } else { if( next[1] != '-' && next.size() > 2 ) { std::string opt = "- "; for( size_t i = 1; i < next.size(); ++i ) { opt[1] = next[i]; m_tokenBuffer.push_back( { TokenType::Option, opt } ); } } else { m_tokenBuffer.push_back( { TokenType::Option, next } ); } } } else { m_tokenBuffer.push_back( { TokenType::Argument, next } ); } } } public: explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { loadBuffer(); } explicit operator bool() const { return !m_tokenBuffer.empty() || it != itEnd; } auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } auto operator*() const -> Token { assert( !m_tokenBuffer.empty() ); return m_tokenBuffer.front(); } auto operator->() const -> Token const * { assert( !m_tokenBuffer.empty() ); return &m_tokenBuffer.front(); } auto operator++() -> TokenStream & { if( m_tokenBuffer.size() >= 2 ) { m_tokenBuffer.erase( m_tokenBuffer.begin() ); } else { if( it != itEnd ) ++it; loadBuffer(); } return *this; } }; class ResultBase { public: enum Type { Ok, LogicError, RuntimeError }; protected: ResultBase( Type type ) : m_type( type ) {} virtual ~ResultBase() = default; virtual void enforceOk() const = 0; Type m_type; }; template class ResultValueBase : public ResultBase { public: auto value() const -> T const & { enforceOk(); return m_value; } protected: ResultValueBase( Type type ) : ResultBase( type ) {} ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { if( m_type == ResultBase::Ok ) new( &m_value ) T( other.m_value ); } ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { new( &m_value ) T( value ); } auto operator=( ResultValueBase const &other ) -> ResultValueBase & { if( m_type == ResultBase::Ok ) m_value.~T(); ResultBase::operator=(other); if( m_type == ResultBase::Ok ) new( &m_value ) T( other.m_value ); return *this; } ~ResultValueBase() override { if( m_type == Ok ) m_value.~T(); } union { T m_value; }; }; template<> class ResultValueBase : public ResultBase { protected: using ResultBase::ResultBase; }; template class BasicResult : public ResultValueBase { public: template explicit BasicResult( BasicResult const &other ) : ResultValueBase( other.type() ), m_errorMessage( other.errorMessage() ) { assert( type() != ResultBase::Ok ); } template static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } static auto ok() -> BasicResult { return { ResultBase::Ok }; } static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } explicit operator bool() const { return m_type == ResultBase::Ok; } auto type() const -> ResultBase::Type { return m_type; } auto errorMessage() const -> std::string { return m_errorMessage; } protected: void enforceOk() const override { // Errors shouldn't reach this point, but if they do // the actual error message will be in m_errorMessage assert( m_type != ResultBase::LogicError ); assert( m_type != ResultBase::RuntimeError ); if( m_type != ResultBase::Ok ) std::abort(); } std::string m_errorMessage; // Only populated if resultType is an error BasicResult( ResultBase::Type type, std::string const &message ) : ResultValueBase(type), m_errorMessage(message) { assert( m_type != ResultBase::Ok ); } using ResultValueBase::ResultValueBase; using ResultBase::m_type; }; enum class ParseResultType { Matched, NoMatch, ShortCircuitAll, ShortCircuitSame }; class ParseState { public: ParseState( ParseResultType type, TokenStream const &remainingTokens ) : m_type(type), m_remainingTokens( remainingTokens ) {} auto type() const -> ParseResultType { return m_type; } auto remainingTokens() const -> TokenStream { return m_remainingTokens; } private: ParseResultType m_type; TokenStream m_remainingTokens; }; using Result = BasicResult; using ParserResult = BasicResult; using InternalParseResult = BasicResult; struct HelpColumns { std::string left; std::string right; }; template inline auto convertInto( std::string const &source, T& target ) -> ParserResult { std::stringstream ss; ss << source; ss >> target; if( ss.fail() ) return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); else return ParserResult::ok( ParseResultType::Matched ); } inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { target = source; return ParserResult::ok( ParseResultType::Matched ); } inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { std::string srcLC = source; std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") target = true; else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") target = false; else return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); return ParserResult::ok( ParseResultType::Matched ); } #ifdef CLARA_CONFIG_OPTIONAL_TYPE template inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { T temp; auto result = convertInto( source, temp ); if( result ) target = std::move(temp); return result; } #endif // CLARA_CONFIG_OPTIONAL_TYPE struct NonCopyable { NonCopyable() = default; NonCopyable( NonCopyable const & ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable &operator=( NonCopyable const & ) = delete; NonCopyable &operator=( NonCopyable && ) = delete; }; struct BoundRef : NonCopyable { virtual ~BoundRef() = default; virtual auto isContainer() const -> bool { return false; } virtual auto isFlag() const -> bool { return false; } }; struct BoundValueRefBase : BoundRef { virtual auto setValue( std::string const &arg ) -> ParserResult = 0; }; struct BoundFlagRefBase : BoundRef { virtual auto setFlag( bool flag ) -> ParserResult = 0; virtual auto isFlag() const -> bool { return true; } }; template struct BoundValueRef : BoundValueRefBase { T &m_ref; explicit BoundValueRef( T &ref ) : m_ref( ref ) {} auto setValue( std::string const &arg ) -> ParserResult override { return convertInto( arg, m_ref ); } }; template struct BoundValueRef> : BoundValueRefBase { std::vector &m_ref; explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} auto isContainer() const -> bool override { return true; } auto setValue( std::string const &arg ) -> ParserResult override { T temp; auto result = convertInto( arg, temp ); if( result ) m_ref.push_back( temp ); return result; } }; struct BoundFlagRef : BoundFlagRefBase { bool &m_ref; explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} auto setFlag( bool flag ) -> ParserResult override { m_ref = flag; return ParserResult::ok( ParseResultType::Matched ); } }; template struct LambdaInvoker { static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); template static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { return lambda( arg ); } }; template<> struct LambdaInvoker { template static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { lambda( arg ); return ParserResult::ok( ParseResultType::Matched ); } }; template inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { ArgType temp{}; auto result = convertInto( arg, temp ); return !result ? result : LambdaInvoker::ReturnType>::invoke( lambda, temp ); } template struct BoundLambda : BoundValueRefBase { L m_lambda; static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} auto setValue( std::string const &arg ) -> ParserResult override { return invokeLambda::ArgType>( m_lambda, arg ); } }; template struct BoundFlagLambda : BoundFlagRefBase { L m_lambda; static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} auto setFlag( bool flag ) -> ParserResult override { return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); } }; enum class Optionality { Optional, Required }; struct Parser; class ParserBase { public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; virtual auto cardinality() const -> size_t { return 1; } auto parse( Args const &args ) const -> InternalParseResult { return parse( args.exeName(), TokenStream( args ) ); } }; template class ComposableParserImpl : public ParserBase { public: template auto operator|( T const &other ) const -> Parser; template auto operator+( T const &other ) const -> Parser; }; // Common code and state for Args and Opts template class ParserRefImpl : public ComposableParserImpl { protected: Optionality m_optionality = Optionality::Optional; std::shared_ptr m_ref; std::string m_hint; std::string m_description; explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} public: template ParserRefImpl( T &ref, std::string const &hint ) : m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} template ParserRefImpl( LambdaT const &ref, std::string const &hint ) : m_ref( std::make_shared>( ref ) ), m_hint(hint) {} auto operator()( std::string const &description ) -> DerivedT & { m_description = description; return static_cast( *this ); } auto optional() -> DerivedT & { m_optionality = Optionality::Optional; return static_cast( *this ); }; auto required() -> DerivedT & { m_optionality = Optionality::Required; return static_cast( *this ); }; auto isOptional() const -> bool { return m_optionality == Optionality::Optional; } auto cardinality() const -> size_t override { if( m_ref->isContainer() ) return 0; else return 1; } auto hint() const -> std::string { return m_hint; } }; class ExeName : public ComposableParserImpl { std::shared_ptr m_name; std::shared_ptr m_ref; template static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { return std::make_shared>( lambda) ; } public: ExeName() : m_name( std::make_shared( "" ) ) {} explicit ExeName( std::string &ref ) : ExeName() { m_ref = std::make_shared>( ref ); } template explicit ExeName( LambdaT const& lambda ) : ExeName() { m_ref = std::make_shared>( lambda ); } // The exe name is not parsed out of the normal tokens, but is handled specially auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); } auto name() const -> std::string { return *m_name; } auto set( std::string const& newName ) -> ParserResult { auto lastSlash = newName.find_last_of( "\\/" ); auto filename = ( lastSlash == std::string::npos ) ? newName : newName.substr( lastSlash+1 ); *m_name = filename; if( m_ref ) return m_ref->setValue( filename ); else return ParserResult::ok( ParseResultType::Matched ); } }; class Arg : public ParserRefImpl { public: using ParserRefImpl::ParserRefImpl; auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { auto validationResult = validate(); if( !validationResult ) return InternalParseResult( validationResult ); auto remainingTokens = tokens; auto const &token = *remainingTokens; if( token.type != TokenType::Argument ) return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); assert( !m_ref->isFlag() ); auto valueRef = static_cast( m_ref.get() ); auto result = valueRef->setValue( remainingTokens->token ); if( !result ) return InternalParseResult( result ); else return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); } }; inline auto normaliseOpt( std::string const &optName ) -> std::string { #ifdef CLARA_PLATFORM_WINDOWS if( optName[0] == '/' ) return "-" + optName.substr( 1 ); else #endif return optName; } class Opt : public ParserRefImpl { protected: std::vector m_optNames; public: template explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} template Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} template Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} auto operator[]( std::string const &optName ) -> Opt & { m_optNames.push_back( optName ); return *this; } auto getHelpColumns() const -> std::vector { std::ostringstream oss; bool first = true; for( auto const &opt : m_optNames ) { if (first) first = false; else oss << ", "; oss << opt; } if( !m_hint.empty() ) oss << " <" << m_hint << ">"; return { { oss.str(), m_description } }; } auto isMatch( std::string const &optToken ) const -> bool { auto normalisedToken = normaliseOpt( optToken ); for( auto const &name : m_optNames ) { if( normaliseOpt( name ) == normalisedToken ) return true; } return false; } using ParserBase::parse; auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { auto validationResult = validate(); if( !validationResult ) return InternalParseResult( validationResult ); auto remainingTokens = tokens; if( remainingTokens && remainingTokens->type == TokenType::Option ) { auto const &token = *remainingTokens; if( isMatch(token.token ) ) { if( m_ref->isFlag() ) { auto flagRef = static_cast( m_ref.get() ); auto result = flagRef->setFlag( true ); if( !result ) return InternalParseResult( result ); if( result.value() == ParseResultType::ShortCircuitAll ) return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); } else { auto valueRef = static_cast( m_ref.get() ); ++remainingTokens; if( !remainingTokens ) return InternalParseResult::runtimeError( "Expected argument following " + token.token ); auto const &argToken = *remainingTokens; if( argToken.type != TokenType::Argument ) return InternalParseResult::runtimeError( "Expected argument following " + token.token ); auto result = valueRef->setValue( argToken.token ); if( !result ) return InternalParseResult( result ); if( result.value() == ParseResultType::ShortCircuitAll ) return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); } return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); } } return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); } auto validate() const -> Result override { if( m_optNames.empty() ) return Result::logicError( "No options supplied to Opt" ); for( auto const &name : m_optNames ) { if( name.empty() ) return Result::logicError( "Option name cannot be empty" ); #ifdef CLARA_PLATFORM_WINDOWS if( name[0] != '-' && name[0] != '/' ) return Result::logicError( "Option name must begin with '-' or '/'" ); #else if( name[0] != '-' ) return Result::logicError( "Option name must begin with '-'" ); #endif } return ParserRefImpl::validate(); } }; struct Help : Opt { Help( bool &showHelpFlag ) : Opt([&]( bool flag ) { showHelpFlag = flag; return ParserResult::ok( ParseResultType::ShortCircuitAll ); }) { static_cast( *this ) ("display usage information") ["-?"]["-h"]["--help"] .optional(); } }; struct Parser : ParserBase { mutable ExeName m_exeName; std::vector m_options; std::vector m_args; auto operator|=( ExeName const &exeName ) -> Parser & { m_exeName = exeName; return *this; } auto operator|=( Arg const &arg ) -> Parser & { m_args.push_back(arg); return *this; } auto operator|=( Opt const &opt ) -> Parser & { m_options.push_back(opt); return *this; } auto operator|=( Parser const &other ) -> Parser & { m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); return *this; } template auto operator|( T const &other ) const -> Parser { return Parser( *this ) |= other; } // Forward deprecated interface with '+' instead of '|' template auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } template auto operator+( T const &other ) const -> Parser { return operator|( other ); } auto getHelpColumns() const -> std::vector { std::vector cols; for (auto const &o : m_options) { auto childCols = o.getHelpColumns(); cols.insert( cols.end(), childCols.begin(), childCols.end() ); } return cols; } void writeToStream( std::ostream &os ) const { if (!m_exeName.name().empty()) { os << "usage:\n" << " " << m_exeName.name() << " "; bool required = true, first = true; for( auto const &arg : m_args ) { if (first) first = false; else os << " "; if( arg.isOptional() && required ) { os << "["; required = false; } os << "<" << arg.hint() << ">"; if( arg.cardinality() == 0 ) os << " ... "; } if( !required ) os << "]"; if( !m_options.empty() ) os << " options"; os << "\n\nwhere options are:" << std::endl; } auto rows = getHelpColumns(); size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; size_t optWidth = 0; for( auto const &cols : rows ) optWidth = (std::max)(optWidth, cols.left.size() + 2); optWidth = (std::min)(optWidth, consoleWidth/2); for( auto const &cols : rows ) { auto row = TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + TextFlow::Spacer(4) + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); os << row << std::endl; } } friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { parser.writeToStream( os ); return os; } auto validate() const -> Result override { for( auto const &opt : m_options ) { auto result = opt.validate(); if( !result ) return result; } for( auto const &arg : m_args ) { auto result = arg.validate(); if( !result ) return result; } return Result::ok(); } using ParserBase::parse; auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { struct ParserInfo { ParserBase const* parser = nullptr; size_t count = 0; }; const size_t totalParsers = m_options.size() + m_args.size(); assert( totalParsers < 512 ); // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do ParserInfo parseInfos[512]; { size_t i = 0; for (auto const &opt : m_options) parseInfos[i++].parser = &opt; for (auto const &arg : m_args) parseInfos[i++].parser = &arg; } m_exeName.set( exeName ); auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); while( result.value().remainingTokens() ) { bool tokenParsed = false; for( size_t i = 0; i < totalParsers; ++i ) { auto& parseInfo = parseInfos[i]; if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); if (!result) return result; if (result.value().type() != ParseResultType::NoMatch) { tokenParsed = true; ++parseInfo.count; break; } } } if( result.value().type() == ParseResultType::ShortCircuitAll ) return result; if( !tokenParsed ) return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); } // !TBD Check missing required options return result; } }; template template auto ComposableParserImpl::operator|( T const &other ) const -> Parser { return Parser() | static_cast( *this ) | other; } } // namespace detail // A Combined parser using detail::Parser; // A parser for options using detail::Opt; // A parser for arguments using detail::Arg; // Wrapper for argc, argv from main() using detail::Args; // Specifies the name of the executable using detail::ExeName; // Convenience wrapper for option parser that specifies the help option using detail::Help; // enum of result types from a parse using detail::ParseResultType; // Result type for parser operation using detail::ParserResult; } // namespace clara #endif // CLARA_HPP_INCLUDED waybar-0.9.0/include/util/command.hpp000066400000000000000000000036301360163675500175520ustar00rootroot00000000000000#pragma once #include #include #include namespace waybar::util::command { struct res { int exit_code; std::string out; }; inline std::string read(FILE* fp) { std::array buffer = {0}; std::string output; while (feof(fp) == 0) { if (fgets(buffer.data(), 128, fp) != nullptr) { output += buffer.data(); } } // Remove last newline if (!output.empty() && output[output.length() - 1] == '\n') { output.erase(output.length() - 1); } return output; } inline int close(FILE* fp, pid_t pid) { int stat; fclose(fp); while (waitpid(pid, &stat, 0) == -1) { if (errno != EINTR) { stat = 0; break; } } return stat; } inline FILE* open(const std::string cmd, int& pid) { if (cmd == "") return nullptr; int fd[2]; pipe(fd); pid_t child_pid = fork(); if (child_pid < 0) { printf("Unable to exec cmd %s, error %s", cmd.c_str(), strerror(errno)); return nullptr; } if (!child_pid) { ::close(fd[0]); dup2(fd[1], 1); setpgid(child_pid, child_pid); execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); exit(0); } else { ::close(fd[1]); } pid = child_pid; return fdopen(fd[0], "r"); } inline struct res exec(std::string cmd) { int pid; auto fp = command::open(cmd, pid); if (!fp) return {-1, ""}; auto output = command::read(fp); auto stat = command::close(fp, pid); return {WEXITSTATUS(stat), output}; } inline int32_t forkExec(std::string cmd) { if (cmd == "") return -1; int32_t pid = fork(); if (pid < 0) { printf("Unable to exec cmd %s, error %s", cmd.c_str(), strerror(errno)); return pid; } // Child executes the command if (!pid) { setpgid(pid, pid); execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); exit(0); } else { signal(SIGCHLD,SIG_IGN); } return pid; } } // namespace waybar::util::command waybar-0.9.0/include/util/format.hpp000066400000000000000000000055211360163675500174250ustar00rootroot00000000000000#pragma once #include class pow_format { public: pow_format(long long val, std::string&& unit, bool binary = false): val_(val), unit_(unit), binary_(binary) { }; long long val_; std::string unit_; bool binary_; }; namespace fmt { template <> struct formatter { char spec = 0; int width = 0; template constexpr auto parse(ParseContext& ctx) -> decltype (ctx.begin()) { auto it = ctx.begin(), end = ctx.end(); if (it != end && *it == ':') ++it; if (*it == '>' || *it == '<' || *it == '=') { spec = *it; ++it; } if (it == end || *it == '}') return it; if ('0' <= *it && *it <= '9') { // We ignore it for now, but keep it for compatibility with // existing configs where the format for pow_format'ed numbers was // 'string' and specifications such as {:>9} were valid. // The rationale for ignoring it is that the only reason to specify // an alignment and a with is to get a fixed width bar, and ">" is // sufficient in this implementation. width = parse_nonnegative_int(it, end, ctx); } return it; } template auto format(const pow_format& s, FormatContext &ctx) -> decltype (ctx.out()) { const char* units[] = { "", "k", "M", "G", "T", "P", nullptr}; auto base = s.binary_ ? 1024ull : 1000ll; auto fraction = (double) s.val_; int pow; for (pow = 0; units[pow+1] != nullptr && fraction / base >= 1; ++pow) { fraction /= base; } auto max_width = 4 // coeff in {:.3g} format + 1 // prefix from units array + s.binary_ // for the 'i' in GiB. + s.unit_.length(); const char * format; std::string string; switch (spec) { case '>': return format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width); case '<': return format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width); case '=': format = "{coefficient:<4.3g}{padding}{prefix}{unit}"; break; case 0: default: format = "{coefficient:.3g}{prefix}{unit}"; break; } return format_to(ctx.out(), format , fmt::arg("coefficient", fraction) , fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : "")) , fmt::arg("unit", s.unit_) , fmt::arg("padding", pow ? "" : s.binary_ ? " " : " ") ); } }; } waybar-0.9.0/include/util/json.hpp000066400000000000000000000011761360163675500171100ustar00rootroot00000000000000#pragma once #include namespace waybar::util { struct JsonParser { JsonParser() {} const Json::Value parse(const std::string& data) const { Json::Value root(Json::objectValue); if (data.empty()) { return root; } std::unique_ptr const reader(builder_.newCharReader()); std::string err; bool res = reader->parse(data.c_str(), data.c_str() + data.size(), &root, &err); if (!res) throw std::runtime_error(err); return root; } ~JsonParser() = default; private: Json::CharReaderBuilder builder_; }; } // namespace waybar::util waybar-0.9.0/include/util/sleeper_thread.hpp000066400000000000000000000032301360163675500211160ustar00rootroot00000000000000#pragma once #include #include #include #include #include namespace waybar::util { class SleeperThread { public: SleeperThread() = default; SleeperThread(std::function func) : thread_{[this, func] { while (do_run_) { signal_ = false; func(); } }} {} SleeperThread& operator=(std::function func) { thread_ = std::thread([this, func] { while (do_run_) { signal_ = false; func(); } }); return *this; } bool isRunning() const { return do_run_; } auto sleep_for(std::chrono::system_clock::duration dur) { std::unique_lock lk(mutex_); return condvar_.wait_for(lk, dur, [this] { return signal_ || !do_run_; }); } auto sleep_until( std::chrono::time_point time_point) { std::unique_lock lk(mutex_); return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; }); } auto wake_up() { { std::lock_guard lck(mutex_); signal_ = true; } condvar_.notify_all(); } auto stop() { { std::lock_guard lck(mutex_); signal_ = true; do_run_ = false; } condvar_.notify_all(); } ~SleeperThread() { stop(); if (thread_.joinable()) { thread_.join(); } } private: std::thread thread_; std::condition_variable condvar_; std::mutex mutex_; bool do_run_ = true; bool signal_ = false; }; } // namespace waybar::util waybar-0.9.0/man/000077500000000000000000000000001360163675500135745ustar00rootroot00000000000000waybar-0.9.0/man/waybar-backlight.5.scd000066400000000000000000000024171360163675500176510ustar00rootroot00000000000000waybar-backlight(5) # NAME waybar - backlight module # DESCRIPTION The *backlight* module displays the current backlight level. # CONFIGURATION *interval* ++ typeof: integer ++ default: 2 ++ The interval in which information gets polled. *format* ++ typeof: string ++ default: {percent}% ++ The format, how information should be displayed. On {} data gets inserted. *max-length* ++ typeof: integer ++ The maximum length in characters the module should display. *rotate* ++ typeof: integer ++ Positive value to rotate the text label. *states* ++ typeof: array ++ A number of backlight states which get activated on certain brightness levels. *on-click* ++ typeof: string ++ Command to execute when the module is clicked. *on-click-right* ++ typeof: string ++ Command to execute when the module is right clicked. *on-scroll-up* ++ typeof: string ++ Command to execute when performing a scroll up on the module. *on-scroll-down* ++ typeof: string Command to execute when performing a scroll down on the module. *smooth-scrolling-threshold* ++ typeof: double Threshold to be used when scrolling. # EXAMPLE: ``` "backlight": { "device": "intel_backlight", "format": "{percent}% {icon}", "format-icons": ["", ""] } ``` # STYLE - *#backlight* waybar-0.9.0/man/waybar-battery.5.scd000066400000000000000000000072131360163675500173720ustar00rootroot00000000000000waybar-battery(5) # NAME waybar - battery module # DESCRIPTION The *battery* module displays the current capacity and state (eg. charging) of your battery. # CONFIGURATION *bat* ++ typeof: string ++ The battery to monitor, as in /sys/class/power_supply/ instead of auto detect. *adapter* ++ typeof: string ++ The adapter to monitor, as in /sys/class/power_supply/ instead of auto detect. *interval* ++ typeof: integer ++ default: 60 ++ The interval in which the information gets polled. *states* ++ typeof: array ++ A number of battery states which get activated on certain capacity levels. See *waybar-states(5)*. *format* ++ typeof: string ++ default: {capacity}% ++ The format, how the time should be displayed. *format-time* ++ typeof: string ++ default: {H} h {M} min ++ The format, how the time should be displayed. *format-icons* typeof: array/object Based on the current capacity, the corresponding icon gets selected. ++ The order is *low* to *high*. Or by the state if it is an object. *max-length* ++ typeof: integer++ The maximum length in character the module should display. *rotate* ++ typeof: integer++ Positive value to rotate the text label. *on-click* ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right* ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up* ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down* ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold* ++ typeof: double ++ Threshold to be used when scrolling. *tooltip* ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. # FORMAT REPLACEMENTS *{capacity}*: Capacity in percentage *{icon}*: Icon, as defined in *format-icons*. *{time}*: Estimate of time until full or empty. Note that this is based on the power draw at the last refresh time, not an average. # TIME FORMAT The *battery* module allows you to define how time should be formatted via *format-time*. The two arguments are: *{H}*: Hours *{M}*: Minutes # CUSTOM FORMATS The *battery* module allows to define custom formats based on up to two factors. The best fitting format will be selected. *format-*: With *states*, a custom format can be set depending on the capacity of your battery. *format-*: With the status, a custom format can be set depending on the status in /sys/class/power_supply//status (in lowercase). *format--*: You can also set a custom format depending on both values. # STATES - Every entry (*state*) consists of a ** (typeof: *string*) and a ** (typeof: *integer*). - The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the ** of the state. Each class gets activated when the current capacity is equal or below the configured **. - Also each state can have its own *format*. Those con be configured via *format-*. Or if you want to differentiate a bit more even as *format--*. For more information see *custom-formats*. # EXAMPLES ``` "battery": { "bat": "BAT2", "interval": 60, "states": { "warning": 30, "critical": 15 }, "format": "{capacity}% {icon}", "format-icons": ["", "", "", "", ""], "max-length": 25 } ``` # STYLE - *#battery* - *#battery.* - ** is the value of /sys/class/power_supply//status in lowercase. - *#battery.* - ** can be defined in the *config*. For more information see *states*. - *#battery..* - Combination of both ** and **. waybar-0.9.0/man/waybar-clock.5.scd000066400000000000000000000022041360163675500170060ustar00rootroot00000000000000waybar-clock(5) # NAME waybar - clock module # DESCRIPTION The *clock* module displays the current date and time. # CONFIGURATION *interval*: ++ typeof: integer ++ default: 60 ++ The interval in which the information gets polled. *format*: ++ typeof: string ++ default: {:%H:%M} ++ The format, how the date and time should be displayed. *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. View all valid format options in *strftime(3)*. # EXAMPLES ``` "clock": { "interval": 60, "format": "{:%H:%M}", "max-length": 25 } ``` # STYLE - *#clock* waybar-0.9.0/man/waybar-cpu.5.scd000066400000000000000000000026031360163675500165050ustar00rootroot00000000000000waybar-cpu(5) # NAME waybar - cpu module # DESCRIPTION The *cpu* module displays the current cpu utilization. # CONFIGURATION *interval*: ++ typeof: integer ++ default: 10 ++ The interval in which the information gets polled. *format*: ++ typeof: string ++ default: {usage}% ++ The format, how information should be displayed. On {} data gets inserted. *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *states*: ++ typeof: array ++ A number of cpu usage states which get activated on certain usage levels. See *waybar-states(5)*. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. # FORMAT REPLACEMENTS *{load}*: Current cpu load. *{usage}*: Current cpu usage. # EXAMPLE ``` "cpu": { "interval": 10, "format": "{}% ", "max-length": 10 } ``` # STYLE - *#cpu* waybar-0.9.0/man/waybar-custom.5.scd000066400000000000000000000104661360163675500172360ustar00rootroot00000000000000waybar-custom(5) # NAME waybar - custom module # DESCRIPTION The *custom* module displays either the output of a script or static text. To display static text, specify only the *format* field. # CONFIGURATION Addressed by *custom/* *exec*: ++ typeof: string ++ The path to the script, which should be executed. *exec-if*: ++ typeof: string ++ The path to a script, which determines if the script in *exec* should be executed. *exec* will be executed if the exit code of *exec-if* equals 0. *return-type*: ++ typeof: string ++ See *return-type* *interval*: ++ typeof: integer ++ The interval (in seconds) in which the information gets polled. Use *once* if you want to execute the module only on startup. You can update it manually with a signal. If no *interval* is defined, it is assumed that the out script loops it self. *signal*: ++ typeof: integer ++ The signal number used to update the module. The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*. *format*: ++ typeof: string ++ default: {} ++ The format, how information should be displayed. On {} data gets inserted. *format-icons*: ++ typeof: array ++ Based on the set percentage, the corresponding icon gets selected. The order is *low* to *high*. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. *escape*: ++ typeof: bool ++ default: false ++ Option to enable escaping of script output. # RETURN-TYPE When *return-type* is set to *json*, Waybar expects the *exec*-script to output its data in JSON format. This should look like this: ``` {"text": "$text", "tooltip": "$tooltip", "class": "$class", "percentage": $percentage } ``` The *class* parameter also accepts an array of strings. If nothing or an invalid option is specified, Waybar expects i3blocks style output. Values are *newline* separated. This should look like this: ``` $text\\n$tooltip\\n$class* ``` *class* is a CSS class, to apply different styles in *style.css* # FORMAT REPLACEMENTS *{}*: Output of the script. *{percentage}* Percentage which can be set via a json return-type. *{icon}*: An icon from 'format-icons' according to percentage. # EXAMPLES ## Spotify: ``` "custom/spotify": { "format": " {}", "max-length": 40, "interval": 30, // Remove this if your script is endless and write in loop "exec": "$HOME/.config/waybar/mediaplayer.sh 2> /dev/null", // Script in resources folder "exec-if": "pgrep spotify" } ``` ## mpd: ``` "custom/mpd": { "format": "♪ {}", //"max-length": 15, "interval": 10, "exec": "mpc current", "exec-if": "pgrep mpd", "on-click": "mpc toggle", "on-click-right": "sonata" } ``` ## cmus: ``` "custom/cmus": { "format": "♪ {}", //"max-length": 15, "interval": 10, "exec": "cmus-remote -C \"format_print '%a - %t'\"", // artist - title "exec-if": "pgrep cmus", "on-click": "cmus-remote -u", //toggle pause "escape": true //handle markup entities } ``` ## Pacman ``` "custom/pacman": { "format": "{} ", "interval": "once", "exec": "pacman_packages", "on-click": "update-system", "signal": 8 } ``` ## Alternate Pacman ``` "custom/pacman": { "format": "{} ", "interval": 3600, // every hour "exec": "checkupdates | wc -l", // # of updates "exec-if": "exit 0", // always run; consider advanced run conditions "on-click": "termite -e 'sudo pacman -Syu'; pkill -SIGRTMIN+8 waybar", // update system "signal": 8 } ``` You can use the signal and update the number of available packages with *pkill -RTMIN+8 waybar*. # STYLE - *#custom-* - *#custom-.* - ** can be set by the script. For more information see *return-type* waybar-0.9.0/man/waybar-disk.5.scd000066400000000000000000000035141360163675500166520ustar00rootroot00000000000000waybar-disk(5) # NAME waybar - disk module # DESCRIPTION The *disk* module displays the current disk space used. # CONFIGURATION Addressed by *disk* *path*: ++ typeof: string ++ default: "/" ++ Any path residing in the filesystem or mountpoint for which the information should be displayed. *interval*: ++ typeof: integer++ default: 30 ++ The interval in which the information gets polled. *format*: ++ typeof: string ++ default: "{percentage_used}%" ++ The format, how information should be displayed. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. *tooltip-format*: ++ typeof: string ++ default: "{used} out of {total} used ({percentage_used}%)" ++ The format of the information displayed in the tooltip. # FORMAT REPLACEMENTS *{percentage_used}*: Percentage of disk in use. *{percentage_free}*: Percentage of free disk space *{total}*: Total amount of space on the disk, partition or mountpoint. *{used}*: Amount of used disk space. *{free}*: Amount of available disk space for normal users. *{path}*: The path specified in the configuration. # EXAMPLES ``` "disk": { "interval": 30, "format": "{percentage_free}% free on {path}", } ``` # STYLE - *#disk* waybar-0.9.0/man/waybar-idle-inhibitor.5.scd000066400000000000000000000026421360163675500206230ustar00rootroot00000000000000waybar-idle-inhibitor(5) # NAME waybar - idle_inhibitor module # DESCRIPTION The *idle_inhibitor* module can inhibiting the idle behavior such as screen blanking, locking, and screensaving, also known as "presentation mode". # CONFIGURATION *format*: ++ typeof: string ++ The format, how the state should be displayed. *format-icons*: ++ typeof: array ++ Based on the current state, the corresponding icon gets selected. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. A click also toggles the state *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. # FORMAT REPLACEMENTS *{status}*: status (*activated* or *deactivated*) *{icon}*: Icon, as defined in *format-icons* # EXAMPLES ``` "idle_inhibitor": { "format": "{icon}", "format-icons": { "activated": "", "deactivated": "" } } ``` waybar-0.9.0/man/waybar-memory.5.scd000066400000000000000000000032261360163675500172300ustar00rootroot00000000000000waybar-memory(5) # NAME waybar - memory module # DESCRIPTION The *memory* module displays the current memory utilization. # CONFIGURATION Addressed by *memory* *interval*: ++ typeof: integer++ default: 30 ++ The interval in which the information gets polled. *format*: ++ typeof: string ++ default: {percentage}% ++ The format, how information should be displayed. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *states*: ++ typeof: array ++ A number of memory utilization states which get activated on certain percentage thresholds. See *waybar-states(5)*. *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. # FORMAT REPLACEMENTS *{percentage}*: Percentage of memory in use. *{total}*: Amount of total memory available in GiB. *{used}*: Amount of used memory in GiB. *{avail}*: Amount of available memory in GiB. # EXAMPLES ``` "memory": { "interval": 30, "format": "{}% ", "max-length": 10 } ``` ## FORMATTED MEMORY VALUES ``` "memory": { "interval": 30, "format": "{used:0.1f}G/{total:0.1f}G " } ``` # STYLE - *#memory* waybar-0.9.0/man/waybar-mpd.5.scd000066400000000000000000000123231360163675500164760ustar00rootroot00000000000000waybar-mpd(5) # NAME waybar - mpd module # DESCRIPTION The *mpd* module displays information about a running "Music Player Daemon" instance. # CONFIGURATION Addressed by *mpd* *server*: ++ typeof: string ++ The network address or Unix socket path of the MPD server. If empty, connect to the default host. *port*: ++ typeof: integer ++ The port MPD listens to. If empty, use the default port. *interval*: ++ typeof: integer++ default: 5 ++ The interval in which the connection to the MPD server is retried *timeout*: ++ typeof: integer++ default: 30 ++ The timeout for the connection. Change this if your MPD server has a low `connection_timeout` setting *unknown-tag*: ++ typeof: string ++ default: "N/A" ++ The text to display when a tag is not present in the current song, but used in `format` *format*: ++ typeof: string ++ default: "{album} - {artist} - {title}" ++ Information displayed when a song is playing or paused *format-stopped*: ++ typeof: string ++ default: "stopped" ++ Information displayed when the player is stopped. *format-disconnected*: ++ typeof: string ++ default: "disconnected" ++ Information displayed when the MPD server can't be reached. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. *tooltip-format*: ++ typeof: string ++ default: "MPD (connected)" ++ Tooltip information displayed when connected to MPD. *tooltip-format-disconnected*: ++ typeof: string ++ default: "MPD (disconnected)" ++ Tooltip information displayed when the MPD server can't be reached. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *state-icons*: ++ typeof: object ++ default: {} ++ Icon to show depending on the play/pause state of the player (*{ "playing": "...", "paused": "..." }*) *consume-icons*: ++ typeof: object ++ default: {} ++ Icon to show depending on the "consume" option (*{ "on": "...", "off": "..." }*) *random-icons*: ++ typeof: object ++ default: {} ++ Icon to show depending on the "random" option (*{ "on": "...", "off": "..." }*) *repeat-icons*: ++ typeof: object ++ default: {} ++ Icon to show depending on the "repeat" option (*{ "on": "...", "off": "..." }*) *single-icons*: ++ typeof: object ++ default: {} ++ Icon to show depending on the "single" option (*{ "on": "...", "off": "..." }*) # FORMAT REPLACEMENTS ## WHEN PLAYING/PAUSED *{artist}*: The artist of the current song *{albumArtist}*: The artist of the current album *{album}*: The album of the current song *{title}*: The title of the current song *{date}*: The date of the current song *{elapsedTime}*: The current position of the current song. To format as a date/time (see example configuration) *{totalTime}*: The length of the current song. To format as a date/time (see example configuration) *{stateIcon}*: The icon corresponding the playing or paused status of the player (see *state-icons* option) *{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option) *{randomIcon}*: The icon corresponding the "random" option (see *random-icons* option) *{repeatIcon}*: The icon corresponding the "repeat" option (see *repeat-icons* option) *{singleIcon}*: The icon corresponding the "single" option (see *single-icons* option) ## WHEN STOPPED *{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option) *{randomIcon}*: The icon corresponding the "random" option (see *random-icons* option) *{repeatIcon}*: The icon corresponding the "repeat" option (see *repeat-icons* option) *{singleIcon}*: The icon corresponding the "single" option (see *single-icons* option) ## WHEN DISCONNECTED Currently, no format replacements when disconnected. # EXAMPLES ``` "mpd": { "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ", "format-disconnected": "Disconnected ", "format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ", "interval": 2, "consume-icons": { "on": " " // Icon shows only when "consume" is on }, "random-icons": { "off": " ", // Icon grayed out when "random" is off "on": " " }, "repeat-icons": { "on": " " }, "single-icons": { "on": "1 " }, "state-icons": { "paused": "", "playing": "" }, "tooltip-format": "MPD (connected)", "tooltip-format-disconnected": "MPD (disconnected)" } ``` # STYLE - *#mpd* - *#mpd.disconnected* - *#mpd.stopped* - *#mpd.playing* - *#mpd.paused* waybar-0.9.0/man/waybar-network.5.scd000066400000000000000000000066651360163675500174230ustar00rootroot00000000000000waybar-network(5) # NAME waybar - network module # DESCRIPTION The *network* module displays information about the current network connections. # CONFIGURATION Addressed by *network* *interface*: ++ typeof: string ++ Use the defined interface instead of auto detection. Accepts wildcard. *interval*: ++ typeof: integer ++ default: 60 ++ The interval in which the network information gets polled (e.g. signal strength). *format*: ++ typeof: string ++ default: *{ifname}* ++ The format, how information should be displayed. This format is used when other formats aren't specified. *format-ethernet*: ++ typeof: string ++ This format is used when a ethernet interface is displayed. *format-wifi*: ++ typeof: string ++ This format is used when a wireless interface is displayed. *format-linked*: ++ typeof: string ++ This format is used when a linked interface with no ip address is displayed. *format-disconnected*: ++ typeof: string ++ This format is used when the displayed interface is disconnected. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *tooltip*: ++ typeof: bool ++ default: *true* ++ Option to disable tooltip on hover. *tooltip-format*: ++ typeof: string ++ The format, how information should be displayed in the tooltip. This format is used when other formats aren't specified. *tooltip-format-ethernet*: ++ typeof: string ++ This format is used when a ethernet interface is displayed. *tooltip-format-wifi*: ++ typeof: string ++ This format is used when a wireless interface is displayed. *tooltip-format-disconnected*: ++ typeof: string ++ This format is used when the displayed interface is disconnected. # FORMAT REPLACEMENTS *{ifname}*: Name of the network interface. *{ipaddr}*: The first IP of the interface. *{netmask}*: The subnetmask corresponding to the IP. *{cidr}*: The subnetmask corresponding to the IP in CIDR notation. *{essid}*: Name (SSID) of the wireless network. *{signalStrength}*: Signal strength of the wireless network. *{signaldBm}*: Signal strength of the wireless network in dBm. *{frequency}*: Frequency of the wireless network in MHz. *{bandwidthUpBits}*: Instant up speed in bits/seconds. *{bandwidthDownBits}*: Instant down speed in bits/seconds. *{bandwidthUpOctets}*: Instant up speed in octets/seconds. *{bandwidthDownOctets}*: Instant down speed in octets/seconds. # EXAMPLES ``` "network": { "interface": "wlp2s0", "format": "{ifname}", "format-wifi": "{essid} ({signalStrength}%) ", "format-ethernet": "{ifname} ", "format-disconnected": "", //An empty format will hide the module. "tooltip-format": "{ifname}", "tooltip-format-wifi": "{essid} ({signalStrength}%) ", "tooltip-format-ethernet": "{ifname} ", "tooltip-format-disconnected": "Disconnected", "max-length": 50 } ``` # STYLE - *#network* - *#network.disconnected* - *#network.linked* - *#network.ethernet* - *#network.wifi* waybar-0.9.0/man/waybar-pulseaudio.5.scd000066400000000000000000000057471360163675500201040ustar00rootroot00000000000000waybar-pulseaudio(5) # NAME waybar - pulseaudio module # DESCRIPTION The *pulseaudio* module displays the current volume reported by PulseAudio. Additionally you can control the volume by scrolling *up* or *down* while the cursor is over the module. # CONFIGURATION *format*: ++ typeof: string ++ default: {volume}% ++ The format, how information should be displayed. This format is used when other formats aren't specified. *format-bluetooth*: ++ typeof: string ++ This format is used when using bluetooth speakers. *format-muted*: ++ typeof: string ++ This format is used when the sound is muted. *format-source*: ++ typeof: string ++ default: {volume}% ++ This format used for the source. *format-source-muted*: ++ typeof: string ++ This format is used when the source is muted. *format-icons*: ++ typeof: array ++ Based on the current port-name and volume, the corresponding icon gets selected. The order is *low* to *high*. See *Icons*. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *states*: ++ typeof: array ++ A number of volume states which get activated on certain volume levels. See *waybar-states(5)* *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *scroll-step*: ++ typeof: float ++ default: 1.0 ++ The speed in which to change the volume when scrolling. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. This replaces the default behaviour of volume control. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. This replaces the default behaviour of volume control. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. # FORMAT REPLACEMENTS *{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name. *{volume}*: Volume in percentage. *{icon}*: Icon, as defined in *format-icons*. *{format_source}*: Source format, *format-source*, *format-source-muted*. # ICONS: The following strings for *format-icons* are supported. If they are found in the current PulseAudio port name, the corresponding icons will be selected. - *default* (Shown, when no other port is found) - *headphones* - *speaker* - *hdmi* - *headset* - *handsfree* - *portable* - *car* - *hifi* - *phone* # EXAMPLES ``` "pulseaudio": { "format": "{volume}% {icon}", "format-bluetooth": "{volume}% {icon}", "format-muted": "", "format-icons": { "headphones": "", "handsfree": "", "headset": "", "phone": "", "portable": "", "car": "", "default": ["", ""] }, "scroll-step": 1, "on-click": "pavucontrol" } ``` # STYLE - *#pulseaudio* - *#pulseaudio.bluetooth* - *#pulseaudio.muted* waybar-0.9.0/man/waybar-states.5.scd000066400000000000000000000021301360163675500172140ustar00rootroot00000000000000waybar-states(5) # OVERVIEW Some modules support 'states' which allows percentage values to be used as styling triggers to apply a class when the value matches the declared state value. # STATES - Every entry (*state*) consists of a ** (typeof: *string*) and a ** (typeof: *integer*). - The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the ** of the state. Each class gets activated when the current capacity is equal or below the configured **. - Also each state can have its own *format*. Those con be configured via *format-*. Or if you want to differentiate a bit more even as *format--*. # EXAMPLE ``` "battery": { "bat": "BAT2", "interval": 60, "states": { "warning": 30, "critical": 15 }, "format": "{capacity}% {icon}", "format-icons": ["", "", "", "", ""], "max-length": 25 } ``` # STYLING STATES - *#battery.* - ** can be defined in the *config*. # EXAMPLE: - *#battery.warning: { background: orange; }* - *#battery.critical: { background: red; }* waybar-0.9.0/man/waybar-sway-mode.5.scd000066400000000000000000000021471360163675500176260ustar00rootroot00000000000000waybar-sway-mode(5) # NAME waybar - sway mode module # DESCRIPTION The *mode* module displays the current binding mode of Sway # CONFIGURATION Addressed by *sway/mode* *format*: ++ typeof: string ++ default: {} ++ The format, how information should be displayed. On {} data gets inserted. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. # EXAMPLES ``` "sway/window": { "format": " {}", "max-length": 50 } ``` # STYLE - *#mode* waybar-0.9.0/man/waybar-sway-window.5.scd000066400000000000000000000025651360163675500202150ustar00rootroot00000000000000waybar-sway-window(5) # NAME waybar - sway window module # DESCRIPTION The *window* module displays the title of the currently focused window in Sway # CONFIGURATION Addressed by *sway/window* *format*: ++ typeof: string ++ default: {} ++ The format, how information should be displayed. On {} data gets inserted. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. # EXAMPLES ``` "sway/window": { "format": "{}", "max-length": 50 } ``` # STYLE - *#window* - *window#waybar.empty* When no windows is in the workspace - *window#waybar.solo* When one window is in the workspace - *window#waybar.* Where *app_id* is the app_id or *instance* name like (*chromium*) of the only window in the workspace waybar-0.9.0/man/waybar-sway-workspaces.5.scd000066400000000000000000000065411360163675500210650ustar00rootroot00000000000000waybar-sway-workspaces(5) # NAME waybar - sway workspaces module # DESCRIPTION The *workspaces* module displays the currently used workspaces in Sway. # CONFIGURATION Addressed by *sway/workspaces* *all-outputs*: ++ typeof: bool ++ default: false ++ If set to false, workspaces will only be shown on the output they are on. If set to true all workspaces will be shown on every output. *format*: ++ typeof: string ++ default: {name} ++ The format, how information should be displayed. *format-icons*: ++ typeof: array ++ Based on the workspace name and state, the corresponding icon gets selected. See *icons*. *disable-scroll*: ++ typeof: bool ++ default: false ++ If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *disable-scroll-wraparound*: ++ typeof: bool ++ default: false ++ If set to false, scrolling on the workspace indicator will wrap around to the first workspace when reading the end, and vice versa. If set to true this behavior is disabled. *enable-bar-scroll*: ++ typeof: bool ++ default: false ++ If set to false, you can't scroll to cycle throughout workspaces from the entire bar. If set to true this behaviour is enabled. *disable-markup*: ++ typeof: bool ++ default: false ++ If set to true, button label will escape pango markup. *current-only*: ++ typeof: bool ++ default: false ++ If set to true. Only focused workspaces will be shown. *persistent_workspaces*: ++ typeof: json (see below) ++ default: empty ++ Lists workspaces that should always be shown, even when non existent # FORMAT REPLACEMENTS *{name}*: Name of the workspace, as defined by sway. *{icon}*: Icon, as defined in *format-icons*. *{index}*: Index of the workspace. # ICONS Additional to workspace name matching, the following *format-icons* can be set. - *default*: Will be shown, when no string matches is found. - *urgent*: Will be shown, when workspace is flagged as urgent - *focused*: Will be shown, when workspace is focused # PERSISTENT WORKSPACES Each entry of *persistent_workspace* names a workspace that should always be shown. Associated with that value is a list of outputs indicating *where* the workspace should be shown, an empty list denoting all outputs. ``` "sway/workspaces": { "persistent_workspaces": { "3": [], // Always show a workspace with name '3', on all outputs if it does not exists "4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exists "5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exists } } ``` n.b.: the list of outputs can be obtained from command line using *swaymsg -t get_outputs* # EXAMPLES ``` "sway/workspaces": { "disable-scroll": true, "all-outputs": true, "format": "{name}: {icon}", "format-icons": { "1": "", "2": "", "3": "", "4": "", "5": "", "urgent": "", "focused": "", "default": "" } } ``` # Style - *#workspaces button* - *#workspaces button.visible* - *#workspaces button.focused* - *#workspaces button.urgent* - *#workspaces button.persistent* waybar-0.9.0/man/waybar-temperature.5.scd000066400000000000000000000044011360163675500202510ustar00rootroot00000000000000waybar-temperature(5) # NAME waybar - temperature module # DESCRIPTION The *temperature* module displays the current temperature from a thermal zone. # CONFIGURATION Addressed by *temperature* *thermal-zone*: ++ typeof: integer ++ The thermal zone, as in */sys/class/thermal/*. *hwmon-path*: ++ typeof: string ++ The temperature path to use, e.g. */sys/class/hwmon/hwmon2/temp1_input* instead of one in */sys/class/thermal/*. *critical-threshold*: ++ typeof: integer ++ The threshold before it is considered critical (Celcius). *interval*: ++ typeof: integer ++ default: 10 ++ The interval in which the information gets polled. *format-critical*: ++ typeof: string ++ The format to use when temperature is considered critical *format*: ++ typeof: string ++ default: {temperatureC}°C ++ The format (Celcius/Farenheit) in which the temperature should be displayed. *format-icons*: ++ typeof: array ++ Based on the current temperature (Celcius) and *critical-threshold* if available, the corresponding icon gets selected. The order is *low* to *high*. *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. *max-length*: ++ typeof: integer ++ The maximum length in characters the module should display. *on-click*: ++ typeof: string ++ Command to execute when you clicked on the module. *on-click-right*: ++ typeof: string ++ Command to execute when you right clicked on the module. *on-scroll-up*: ++ typeof: string ++ Command to execute when scrolling up on the module. *on-scroll-down*: ++ typeof: string ++ Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. # FORMAT REPLACEMENTS *{temperatureC}*: Temperature in Celcius. *{temperatureF}*: Temperature in Fahrenheit. # EXAMPLES ``` "temperature": { // "thermal-zone": 2, // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", // "critical-threshold": 80, // "format-critical": "{temperatureC}°C ", "format": "{temperatureC}°C " } ``` # STYLE - *#temperature* - *#temperature.critical* waybar-0.9.0/man/waybar-tray.5.scd000066400000000000000000000006701360163675500166770ustar00rootroot00000000000000waybar-tray(5) # NAME waybar - tray module # DESCRIPTION _WARNING_ *tray* is still in beta. There may me bugs. Breaking changes may occur. # CONFIGURATION Addressed by *tray* *icon-size*: ++ typeof: integer ++ Defines the size of the tray icons. *spacing*: ++ typeof: integer ++ Defines the spacing between the tray icons. # EXAMPLES ``` "tray": { "icon-size": 21, "spacing": 10 } ``` # STYLE - *#tray* waybar-0.9.0/man/waybar.5.scd000066400000000000000000000077021360163675500157250ustar00rootroot00000000000000waybar(5) # NAME waybar - configuration file # DESCRIPTION The configuration uses the JSON file format and is named *config*. Valid locations for this file are: - *$XDG_CONFIG_HOME/waybar/config* - *~/.config/waybar/config* - *~/waybar/config* - */etc/xdg/waybar/config* A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config. Also a minimal example configuration can be found on the at the bottom of this man page. # BAR CONFIGURATION *layer* ++ typeof: string ++ default: bottom ++ Decide if the bar is displayed in front of the windows or behind them. *output* ++ typeof: string|array ++ Specifies on which screen this bar will be displayed. *position* ++ typeof: string ++ default: top ++ Bar position, can be *top*, *bottom*, *left*, *right*. *height* ++ typeof: integer ++ Height to be used by the bar if possible. Leave blank for a dynamic value. *width* ++ typeof: integer ++ Width to be used by the bar if possible. Leave blank for a dynamic value. *modules-left* ++ typeof: array ++ Modules that will be displayed on the left. *modules-center* ++ typeof: array ++ Modules that will be displayed in the center. *modules-right* ++ typeof: array Modules that will be displayed on the right. *margin* ++ typeof: string ++ Margins value using the CSS format without units. *margin-* ++ typeof: integer ++ Margins value without units. *name* ++ typeof: string ++ Optional name added as a CSS class, for styling multiple waybars. # MODULE FORMAT You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat). e.g. ``` "format": "{}" ``` # MULTIPLE INSTANCES OF A MODULE If you want to have a second instance of a module, you can suffix it by a '#' and a custom name. For example if you want a second battery module, you can add *"battery#bat2"* to your modules. To configure the newly added module, you then also add a module configuration with the same name. This could then look something like this *(this is an incomplete example)*: ``` "modules-right": ["battery", "battery#bat2"], "battery": { "bat": "BAT1" }, "battery#bat2": { "bat": "BAT2" } ``` # MINIMAL CONFIGURATION A minimal *config* file could look like this: ``` { "layer": "top", "modules-left": ["sway/workspaces", "sway/mode"], "modules-center": ["sway/window"], "modules-right": ["battery", "clock"], "sway/window": { "max-length": 50 }, "battery": { "format": "{capacity}% {icon}", "format-icons": ["", "", "", "", ""] }, "clock": { "format-alt": "{:%a, %d. %b %H:%M}" } } ``` # MULTI OUTPUT CONFIGURATION ## Limit a configuration to some outputs ``` { "layer": "top", "output": "eDP-1", "modules-left": ["sway/workspaces", "sway/mode"], ... } ``` ``` { "layer": "top", "output": ["eDP-1", "VGA"], "modules-left": ["sway/workspaces", "sway/mode"], ... } ``` ## Configuration of multiple outputs Don't specify an output to create multiple bars on the same screen. ``` [{ "layer": "top", "output": "eDP-1", "modules-left": ["sway/workspaces", "sway/mode"], ... }, { "layer": "top", "output": "VGA", "modules-right": ["clock"], ... }] ``` ## Rotating modules When positioning Waybar on the left or right side of the screen, sometimes it's useful to be able to rotate the contents of a module so the text runs vertically. This can be done using the "rotate" property of the module. Example: ``` { "clock": { "rotate": 90 } } ``` Valid options for the "rotate" property are: 0, 90, 180 and 270. # SUPPORTED MODULES - *waybar-backlight(5)* - *waybar-battery(5)* - *waybar-clock(5)* - *waybar-cpu(5)* - *waybar-custom(5)* - *waybar-idle-inhibitor(5)* - *waybar-memory(5)* - *waybar-mdp(5)* - *waybar-network(5)* - *waybar-pulseaudio(5)* - *waybar-sway-mode(5)* - *waybar-sway-window(5)* - *waybar-sway-workspaces(5)* - *waybar-temperature(5)* - *waybar-tray(5)* waybar-0.9.0/meson.build000066400000000000000000000154771360163675500152010ustar00rootroot00000000000000project( 'waybar', 'cpp', 'c', version: '0.9.0', license: 'MIT', default_options : [ 'cpp_std=c++17', 'buildtype=release', 'default_library=static' ], ) cpp_args = [] cpp_link_args = [] if get_option('libcxx') cpp_args += ['-stdlib=libc++'] cpp_link_args += ['-stdlib=libc++', '-lc++abi'] cpp_link_args += ['-lc++fs'] else cpp_link_args += ['-lstdc++fs'] endif compiler = meson.get_compiler('cpp') git = find_program('git', required: false) if not git.found() add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'cpp') else git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip() git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch) add_project_arguments('-DVERSION=@0@'.format(version), language: 'cpp') endif if not compiler.has_header('filesystem') if compiler.has_header('experimental/filesystem') add_project_arguments('-DFILESYSTEM_EXPERIMENTAL', language: 'cpp') else add_project_arguments('-DNO_FILESYSTEM', language: 'cpp') warning('No filesystem header found, some modules may not work') endif endif add_global_arguments(cpp_args, language : 'cpp') add_global_link_arguments(cpp_link_args, language : 'cpp') thread_dep = dependency('threads') libinput = dependency('libinput') fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep']) spdlog = dependency('spdlog', version : ['>=1.3.1'], fallback : ['spdlog', 'spdlog_dep']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0']) dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk')) giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk')) jsoncpp = dependency('jsoncpp') sigcpp = dependency('sigc++-2.0') libnl = dependency('libnl-3.0', required: get_option('libnl')) libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl')) libpulse = dependency('libpulse', required: get_option('pulseaudio')) libudev = dependency('libudev', required: get_option('libudev')) libmpdclient = dependency('libmpdclient', required: get_option('mpd')) gtk_layer_shell = dependency('gtk-layer-shell-0', required: get_option('gtk-layer-shell'), fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep']) systemd = dependency('systemd', required: get_option('systemd')) prefix = get_option('prefix') conf_data = configuration_data() conf_data.set('prefix', prefix) if systemd.found() user_units_dir = systemd.get_pkgconfig_variable('systemduserunitdir') configure_file( configuration: conf_data, input: './resources/waybar.service.in', output: '@BASENAME@', install_dir: user_units_dir ) endif src_files = files( 'src/factory.cpp', 'src/AModule.cpp', 'src/ALabel.cpp', 'src/modules/memory.cpp', 'src/modules/battery.cpp', 'src/modules/clock.cpp', 'src/modules/custom.cpp', 'src/modules/cpu.cpp', 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', 'src/modules/temperature.cpp', 'src/main.cpp', 'src/bar.cpp', 'src/client.cpp' ) if true # find_program('sway', required : false).found() add_project_arguments('-DHAVE_SWAY', language: 'cpp') src_files += [ 'src/modules/sway/ipc/client.cpp', 'src/modules/sway/mode.cpp', 'src/modules/sway/window.cpp', 'src/modules/sway/workspaces.cpp' ] endif if libnl.found() and libnlgen.found() add_project_arguments('-DHAVE_LIBNL', language: 'cpp') src_files += 'src/modules/network.cpp' endif if libpulse.found() add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp') src_files += 'src/modules/pulseaudio.cpp' endif if dbusmenu_gtk.found() add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp') src_files += files( 'src/modules/sni/tray.cpp', 'src/modules/sni/watcher.cpp', 'src/modules/sni/host.cpp', 'src/modules/sni/item.cpp' ) endif if libudev.found() add_project_arguments('-DHAVE_LIBUDEV', language: 'cpp') src_files += 'src/modules/backlight.cpp' endif if libmpdclient.found() add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') src_files += 'src/modules/mpd.cpp' endif if gtk_layer_shell.found() add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp') endif subdir('protocol') executable( 'waybar', src_files, dependencies: [ thread_dep, client_protos, wayland_client, fmt, spdlog, sigcpp, jsoncpp, libinput, wayland_cursor, gtkmm, dbusmenu_gtk, giounix, libnl, libnlgen, libpulse, libudev, libmpdclient, gtk_layer_shell ], include_directories: [include_directories('include')], install: true, ) install_data( './resources/config', './resources/style.css', install_dir: join_paths(get_option('out'), 'etc/xdg/waybar') ) scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) sh = find_program('sh', native: true) mandir = get_option('mandir') man_files = [ 'waybar.5.scd', 'waybar-backlight.5.scd', 'waybar-battery.5.scd', 'waybar-clock.5.scd', 'waybar-cpu.5.scd', 'waybar-custom.5.scd', 'waybar-disk.5.scd', 'waybar-idle-inhibitor.5.scd', 'waybar-memory.5.scd', 'waybar-mpd.5.scd', 'waybar-network.5.scd', 'waybar-pulseaudio.5.scd', 'waybar-sway-mode.5.scd', 'waybar-sway-window.5.scd', 'waybar-sway-workspaces.5.scd', 'waybar-temperature.5.scd', 'waybar-tray.5.scd', 'waybar-states.5.scd', ] foreach filename : man_files topic = filename.split('.')[-3].split('/')[-1] section = filename.split('.')[-2] output = '@0@.@1@'.format(topic, section) custom_target( output, input: 'man/@0@'.format(filename), output: output, command: [ sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output) ], install: true, install_dir: '@0@/man@1@'.format(mandir, section) ) endforeach endif clangtidy = find_program('clang-tidy', required: false) if clangtidy.found() run_target( 'tidy', command: [ clangtidy, '-checks=*,-fuchsia-default-arguments', '-p', meson.build_root() ] + src_files) endif waybar-0.9.0/meson_options.txt000066400000000000000000000020401360163675500164520ustar00rootroot00000000000000option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.') option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features') option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features') option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio') option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit') option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon') option('out', type: 'string', value : '/', description: 'output prefix directory') option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support') waybar-0.9.0/package/000077500000000000000000000000001360163675500144145ustar00rootroot00000000000000waybar-0.9.0/package/archlinux/000077500000000000000000000000001360163675500164115ustar00rootroot00000000000000waybar-0.9.0/preview-2.png000066400000000000000000000124731360163675500153560ustar00rootroot00000000000000PNG  IHDRI\PLTE$<#:%=$: %<%< &=f̙)$;dr}BL\#;$:Y.q%;$="9AK\ &>#<!:!=$<";BL]@K]#<8;9AK[9!'> ;>GZ*\E/fLbœ<~^U_7dɖt@Di3nSIq  /OyYoB___~ppp 3#69FkWc-bI*iݴ ϰܙ<1쐽Ϩ +_Go{ */CXG'`%=KmlV0xM[ɻ PTaTǡc#W]cx̢ IDATx [p@DvY rG*ZmW[Z>睙IfͱJ9W:bD1V9C,$1J eTCVs c‚9Cu)&)=C9ޱ{X)BO-M@x<|h~d0mqp7bD{,58hFWml΄ Z㬃hv뀽MqO``^mU砳v`; `S-j{iQ7Xq`@}p'ޚЫDM_N3>)58lVH1jd+gXN~ 6l&{ wfsO3z:FKFJI[nE6Np =$pNv6-a9Dzu8󋷼C~gixwwyY{ /$D7F_Oѣ;?`4}%v+< ucA1Ԉ}ϊmM^'a(rzקS#O}_+jR@Jg.PR0V-$-G`v`6Io+Z0%L0%#!]]XWtލzMdv_Cع˹`ky|>痚p/N /k#u[!-oG]eYf2 w.`܎톮Eϼ/ /g޻\WQ+-U~N|0wB^/QX@f1"]+>'sH! "`d: /tcY0SI*軀hū} >Jk ]ka-iS9zj`C0~^_^Ǿ|;???wB0 2=1pY>^kˮhEȂRT[ =!3b i-Ί3"g{jt2 )0DAYHUSsCj5ITÍep$N"\*Fn&sxe(;U?Ocd ,i.,T)A+Aϐ6*vw``cR˺|YL1oT z|^r8v1~FEC| ł|h ]ZLlG(F 7\JDײp &JP|n T/={n.@{?|韫7Wb}j[!%3]qm^ٖ2xʊlYP^z%-pFhWDq+bjT#@Y5%:!_eGO%s@*\ C$⍇q d2>)p..GR`Ox* څ ؏qA\efLR2^lfw:"]R(SomWV5]D ¼gcQ/`Z̔QӞN\Nx N<~xP>_x?x)7Kl8[cF]Zk,͢8:-BQFq m݃:# pvv'[oVo>G3 3 !ho0R˕0,>[6O] |tQpp /g>+Y}IHˁ,nW,C|kޑp:N9?)֝H7Jp2Jt@w.s'(P Sa076H+=N.><d{XBCܚat :NhܜBpmp/{MJӐ;A V|-]Tߦ`xB,T9 -Z?lb?uP|=ّ ]oXjhi^;J, oX,Q䳰Rb4&9] uR^A&6E]]XQ7-]GPKqyxt3ifu[BWBϕBLx{4^c12 P lRY]עE!>!^|Q8$p%P ωmW^Ꞇ`ZXšHOaucFQ5-v k9N&2*cDQ#֏?B?@VH[rP-T)F:Dw],4UX!^$ po:rvӯ^)  4*B4JN":I9q0<sx.hg!p ^pK(_܃a*hc$ I D2ҠYn MWתT@5)dp?%20`Pm4N|v gk{ ]d94u=( o16eu+ b Zԧ3X`+ ܽK$K8`F|-p;WD]g0Ţs~@ ?f2<+ ?!aW6F, cg,߳ZeEׅ8ߊ<ѷ8-`$|8`% C~sq4$6~I+l5/lޯƌdWc`ʪsWqZ3sJar,Qԙr.޴!Uuή?#Q۰Oc٠٩߀u`m;;،C `)bB8: TΒdrqckGҔj ?5a|Cm]r)Jл50< Khr(2둿4HYFiSZ_n9cشA h ?Lpw `5Emf43g x@{`q 4US>p ?>>)< .ܗ%ha0ᵘ‚a&s0poE?`NL>e o{~PPW{0< `ƳxYF CFV'0spܠn-[<,Ħ- =$<066-_R ,z3'Vr5_<6qk-Uǫ!`@0*CP0 CP0s:C0 CpL߇!`@0]\4IJߴ2']fM+QϟXE>8ݾL|Ms;4?9g.QƷY+$XB1ˎW x_ e_G{JR$)lj' \RP?Z}Ar1MvjH==LQ痖%JbI cMϨ"Nn*'Z9Vg+{~[|IDEmZ)-TB/$`>;Mo*lDz" w|u,K.&7C!14l XvْZUz. ɶ/4tUY?JEM}؜{& VANb^To߈~I꼛,.*\椥Dyf)ޔY V^?D*\LvꄦlWt*}N-І /?~7F*x*zXT'}z!j%7Zxo>: 0zr讵{*jd4uT[.5ֈEGҥ >],B3E1AOPi *έhEҬ^kw]:L?O4+=+GiU]:YO6\O.!`8K Oݾ#6o3j5z ӪNu61~=o?lr\+9w 3jڍ)2ZLݾn? `J"B_KXbO}6wQx?Lo3jGfWZޣ&`hBCIuM?iLki3 q#i8?;sMohʻYPR9?GǣXʊ=b O֠{ҮfrdO&p:g9SB]s=Mk9HA,`xAz<.%J8A{y`N]8Tq3< e`fvpkWxjI7nmkr5ӹlTcaLuj)mxA뉧n %79_ >}E]t| [w}{0 -xaCMV'JIcq9r?dHz(lynS$XKj-'~7*Z^d/1O M/l;5t DzJfTKXtvޣ%_{$Tt|{J,_Wh6fx{RUXUxۈ Xb T ֩+* Fժ2ƀPj,?Qc *:Csp~4y<^p [dj a%(6I_~u_eҦόD/ȷAu0Ͷ-K |\Y\Vb0Yj/zD}^T8W8J%( ë_^䊦I*\uPL㧱>/D8ק־ERgORV{{g&`r>}?]` j[Dg@]jW4Y`Fh^)gRZ%X 8AEmjs]" Xm|JI `Ue,TTKzj(ˆ4R-ٰkȮPQ_n5vsO}z_o_q4T"W㧱>/WI% XlL}0wTh1%-Wi4W*0/@ׯYxQ^iT4TV:+b o3Im֫$5 `Ҥјۧ XHbPQ[TjI?Tp-kjFiOu"T%%@+_(Cω^B}&1huN/^tUue%'Q5m 7ɞ/cHHƠ\U*mx|>4yުgxp*5Ҷ(YB6 N`bCANܧO0!` q0"xSa0tj7H8!`I 0HuQ?$%"U `,N~c!vP[y Xt d sZUtJ_0: n! Zļ `+00b9C $&7 q<YcrԼ Sakvv *eȢ[;y;p.9$3dN]|}m2@v3fOF/แR͌ t x&\Bs2&%V5c d\3dmxG^>]F1en_g>Rlwy%~QM[+uכ_K;貧F#=<n? _]{>_>>:6: '`㯩{?ϡR/ u# e߾>\-g~y7?/?kI]x 6tIg߿>wC_~,z}3v cwbܽ^rp$'/aK/N|6rlC'mo$ixAFM@-MYs ΙJlɥ=#8oDPvDD% ԜαHK/ iW׏|9"NQd<׍)r;څ|`s. iw;:E`UKCZTjUPCJ#1,$'֛Yg Ҕ.AUUr+ jKonumVmt> G-OLf-[ MK'lJ2x|SԨaԘ^06$`OAw4h7֧& E"b|+H ^NyQb EjVX>Z6v7 ƠIu#fwF]EqӶ+aez##$\U{Thf+u[f3`\kU'vqܻD'`kVȦmy٭f b݆^Ӣ/KnTmEҊV7L,ʱASK{M [\c8͊u ӠLE QIg QoL߳U-k`jRxz?-< /+WV~{x8FDbi 9:We3ێ>)M4g V3ÝDrTĎoQ]xK7EZ(h̓ZdTt;GDsNn|tӷX׹Vac0pլLbuǒF"FF[&OvuNJ=[ [ɕjTsU-ʤO[ly8`\+40[2qlDƾː,%33:VHKGRKjES:bvwE#>)ꖊ[蔀}n usٳQ"Tm)2{Z&dV͋ujٌs=b&a6WYU,d:̤p_.76[f|+vcЫ݈x4;bLeRk 8KjGp-Y5ERӶ][rIM xҍ_\15L#pSK3A] , II_IYWefD5d&L,ݺi*QF]4| 7-熬[upoE=X?7qgQ6}"B['ݻ*Q3\ǡWxC{0?г0Q4aY JaBBpNWū x>rJH,-U!`x z*7(H?3in>FU^k ~j^GȺNt-@mzxuc8`pb 뗀!  ``D`TK3TW*y z l"Wy)8`X# _UB=/B v/ `$`k `0{5= 8LpcHܳWA?qV#l?WT~{O#s 1czb[֥ߡ޹-8$v0 ` <kכA#ϡs0 Oʳͷ5] =;~?_$\2B#BvGlYllU\j?7 ر/%PU5{{h2ʪ(!Lkӷ3Oboƻ~;\M_7{ :>>Y .8'błoߟ#Q~2$w.’.nf0,=hwv?3 ߄& Wzӭ. ɡo mb /GS0%(lP]܅禸Pkg:A^'8`8bWg]Wͫf &- ZelY' 꾵׬xlݱl2)jH="F(T&Z,h:!мJWՂ-mGj%jUqB.VuŚYKk'HVV#9S}YM{j6sm2RO䴌u[j -fN"JΔe8(oTGp11|>>~QbX~;\·Iïכy7#pȘcyMZ}*-JZ-NK]*L{0-qZM"5-]"b&Fî: KTcV=m+$6fU+Dh(6FR'LfQ!d,U4Q2f]L$;ĈZ#IUK5Uλ./~Lx䛃wAe_IesSWj `q\s>=ӄ+R0]4`>ZuD Xdj3gW$RGJQ[H!RtH4ƪoZe}) |U)-1BKp\$bge()_#$SqStvd/E4`͈(rF$DJ#oӛ mh;_J¢|8.wAFtn% x9 \KhȀ@:%6z ,B {oDu-U2A*_7۰lۖhn{Ӗm m2* iæe<efto?H%"A=\7K!`a25oD6 ߸'$,/&NI~f`fwaaXi&$ Xp!$`.`;:cZ}ţ6|H8^YD[dhʷ0YHQ 캲P8U eOHĬdnjk_ ֎MI|a3~{,أf*^x!"nC!MSz҄l5~wA_Yb _,r6f+u_ƙ13Kkynff7Bߨv[gM~ zh9Q<9ZDE+؄]e5h(ۃ J3 RX>I+CX$* T̯0FqjY_ rP}c[;S|ojs 3>,?s',M9=Q 㣠2]$nl!WZ8~'r'p02>B*lu6퓘B "م ,}͂lAXժBwӤ˹-0o,4F_sij994żemmBhRv|-Sf3#j7/FB:ՐܟoGxBz .<  ؞-茩(Tb`ëƟ98T40~pSp~/8ڋϐAc %&+:!ҟ k}z^{.||xLX(`"0 ,Ro`$``Y%AX0>W]-Uvo. Pl U) 5- `t|R.@y׀#L)00 ̿5a"њylvTL9^}#M]@`Adqp( N:1af!` 』(Jcʮ_Xx6բXHiTxsq X@s[a``YW0l,F ES*x# N\&ٕ <6noKߕ- ׀WZ_M胆q_SA Dž_uB, kVi-{?ZO0/W4i#6يd˪fҴik$awrl_4 k%R"IvY7r!^, $h̴gA,Zςb; o°N!՜ dϳ6V-fj@s-iZI+;}pw|?)n3rBiC4W~)̒;/8.H>?葆}r*rfo&Ia˜%C7=c]OY 9Q8b Yh}:#w,`9vcbs%ۯSL=mYc;hPᲚ$]??]Ћ$` [a[]{̒7tAdxZJޠW]ХfOw\z<~nlRӈ{ 4f*JCE2pv-D*詀*9{j/wAJX-u[TAaźJ 8d̯$3*|4aM[F+:"3LҵB6Iq;h[Af xy;6U,zy"]9Ka]^E!N2Gf!_.N5F8^uAak_1H| 6-|*!Uӈ/&vĉ8^wA `VG&슀R,UJ x2^i{W]z+TEXq;Ly|`luvZ#>[X_EEU!Yć!2b/ IT:M`km, A,2pfj_8%"qQ-\? n^n;\ '-?ڤ4<<NE'c_N&!Ǘ !Qǘ?Kw.`WӗO+`!`~_q|Qo-v4ml[8%X BiiQ޵b 0 NGDBhcq9i&@ax:ᐩ[x8B y5,"% ~8?Wf0xl_OG ~^I! ¿[858-. X[50 s闉W~t1'p8NiBiA5XĚp!_n̼; $>f2wxNbN%i\Z{d8Y~>IÙ82+ޡNNP+e5* !vA sul_A"V:J::궠*sk_B O oyfoW0F>>fvN7̾o&c!_ obC[&c$` `DW@ `@.&00 c^_K_ F`@>L0vn% o"G7JL0n27| 82)00gNOC"o.06 0N kLƅ&B/ Ƅ7nYݚm]B 1@|ik}<|Ls̄D"`cSD"HOX'$D"})k0D"HgpLD"HOWb $D"}IXI$D4OE&H$?I$D|'D"VvjUtϒ^|r9L`0D"=E{i^S^?ճ4sћVws I'I$҉T4י|YiYw./x R&H zS=K3W&}R^%$,D>5(7r\8h3L/οtsCT#\n$\ nZPl|z`ǹ/j{:w lg",|ۛTUb&L.OsՏچգyېsƞqbT9=o8M#O@ꢪ,.+q*8KWl]@oA=fBuO`%FB.+gr%w>:Эn`urW+ZpZhyc!apE[dqŊƺ̫h*"h·;repbjY2k! ls z`h\hK\T}ZȃmGzчP[r|׊4\p߇[UG#* y(.䴆xSǝ"uﱱo-sxe[ ⷭYŒ>kmXUfFLvb뗓;r4f۳cq6A.NL0XCƪQ6r sWZFP=$n%7acd*Ȝ8pXu`(Zxx1qn'zuetfĮ5f3Q?௑z极הJqh?FuC"s} BpWF*iTc˳lq$ p\L7`"y[n=G|JTz[wq8knV>1 mn ipy Gn݉HpK5c[ᄱdb,8WIXX_X|q%vӿxXmMkUܬ[Z@Yw;ssc?ðe5p۲Qw1EyzSeI3JZMpG㨵y 09` kMhh06p]UfB1dwJ"szMI7d/2p c+AÐ2 %!Dͨ~3=pm6k]\M q0MFv`qψYժe,zs cO(?tq(Z<8fA# u &vJO^ưM`r2SpCW5$ܪ@+q7ʽ6pF;=};gÙx"48|8O3ae1\7?>3`)*{Nh&{ E1x#M;<%cH7w3ŏIyd33](,\.K`\*BydrIY7ep>Ǜp-V>gp-uO 8 K2LRN Y$%@ϿǞGEK$`a`*uQ00d>C%c/0 xG~YY  lC|yGC R0 +{f7+Oo| WNoЩpec];t*DB0 Q0  X~H0  _!`200 >?#C0 [ C0 %.C0 H>[ `+`\񪴿H[]^?oEE1Xl`_#X/:N+dzpBX~ t䢢a; u"of0j*jsAhfѨ_֛lR+d_p\Pb.MJ=TgXN)5׮,)D>awގ*-%i[v-5/cd \-0By7+`{3~㍭Ȟץ_W+1"*uuaxJq|tgqM%͔ie ЫMU-7O/R~ӼME_'DaV5y| CC(`?WQy<'yl:v& m#?|&g\ΌNƬ\GGnפ=e[&Xo<8GV%rtϵy'hu#^4tQYNN6xjUE1)="ac#JԦDZ4vCB?a4fT{J5ɾ+-dloh^*5EYLv#%S{^l쏂^ov>/ nr 8IIj:/^؞z5T&co\ HMU&. `omQJ8CmB֮(,}cYAH4T}$Dvh6ܖ;}K&chyXFhƕRvD"ĸaw0ToXK_ih)9ÊurT5#آTibqׯ,dL ftX rù-B,QUM&=z)I dKqFK^6Ohᙚmd۲]+ƌ1bՍXPB+ pX3Y!#aiHJX!*"V|UK^lzjZ1()O8VdD|j=uhsvb[jk"iۃ~ ܒҦJmi|r!5n ν}fɏ VOXAy;Ơ!]űoKQo5T^tGW:m/ҾZۢܩ۷(ę\]yǕM5s)Slh 'aA1c.?(c1#{(qDH N4DДJ{#v}RWqoC|k Cgy;n{\.w"G_"g>'MoG}=Ia\$`}|{{se_0  NH? C0|lݑ}LOO0 '`0nC0|5hNߧ/0 '`/ Cp^P  Cp,(>!`] l?"|8/pp84l7 ZnM`e>{|0UÏ?YQ_pi߅񖀿Vyfw1$$`,W7p"~IR!A2Ub *E_LH6,/^~e,GW\2E6^q seĦ0l-єN&rR`%.< 8E\'cMy,(SL@8i6J3XA y@'ٱ~f ƅ0džze̕Fjgt0f.HHk\~$ɹ,6Z^A2qP )W]zA= ӽ͚%Vs~9ɈU yd[۬\1sN,otfd]v܄U BĺjuC7g )B< OU x9@ojC=\jvlȄk[ yJ"Yl(js,JZckTTن5BRi X2lbp!ȌeD)y1Q'Z1@7,W'`=y>ܼ$+?.yo~iD5DWS y,=hJbZ:SK9߰ӆXx>yܯ>o(J9[)z {-Jk|xqW. -ᚍ+web,/_[Z.Ե/Z`9{Oࢣyww\h}'9/ON,Lo_dY-zsl#.&@o4pj8~#I_5Ǩ,&o3jL17h]r8o~N^enWj'r‚;[WqZ ժ 8'8yy `8r?,y~yy9`%9֏# u6H`8M?':հL@qWV<W"p?hfq%}`8E[i߄O@0df 3p_Xhp p_U)'Caq患so϶{dQ@prЫ?oߞPdY@ft6b#}<>fx<{2ȻNME:qb$vB<>mw˴T}يG\yDI['ud}y9'%p߰ :Q6" 1 :hx^;& :c/hc!I6qp"s/k5T_9>V&|e 0Wo0<瀙8Y3p C_ 05f @ @ `0~ 1F<`||`|T`e@Y"XA U`V `m*M 0k+Vi(_ 0k+Vi04k+Vi%X VW ULm-etOS~4tY/ok 85<o0ࣥ}]9*9,Nw~|ʝ;^aYW;{__B,o__`ћu ԣ6Ն93!8wԇ76<6S@,p>u/ן#>ͨÁηeT=p93z_tɄf wx_ Yk SAKg0"Dh [Dg9=pPWwNKRtž ;jGʖ.S ]ڷ_ 0ݯ/Y;Li{K_,yFٱu0hMO qELף9XU{;{\de4q;RsҲ;51w-k y{Oݢf፰ Y\Н[!Ҏ.:eֱ~Ip3xN >XN9 po[Ô3v/b&B3kcYj U0Wa$*0{Rgyjm5wV,'$IENDB`waybar-0.9.0/protocol/000077500000000000000000000000001360163675500146625ustar00rootroot00000000000000waybar-0.9.0/protocol/dbus-menu.xml000066400000000000000000000050701360163675500173050ustar00rootroot00000000000000 waybar-0.9.0/protocol/dbus-status-notifier-item.xml000066400000000000000000000037651360163675500224460ustar00rootroot00000000000000 waybar-0.9.0/protocol/dbus-status-notifier-watcher.xml000066400000000000000000000035141360163675500231350ustar00rootroot00000000000000 waybar-0.9.0/protocol/meson.build000066400000000000000000000065611360163675500170340ustar00rootroot00000000000000wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') wayland_scanner = find_program('wayland-scanner') # should check wayland_scanner's version, but it is hard to get if wayland_client.version().version_compare('>=1.14.91') code_type = 'private-code' else code_type = 'code' endif wayland_scanner_code = generator( wayland_scanner, output: '@BASENAME@-protocol.c', arguments: [code_type, '@INPUT@', '@OUTPUT@'], ) wayland_scanner_client = generator( wayland_scanner, output: '@BASENAME@-client-protocol.h', arguments: ['client-header', '@INPUT@', '@OUTPUT@'], ) client_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ] client_protos_src = [] client_protos_headers = [] foreach p : client_protocols xml = join_paths(p) client_protos_src += wayland_scanner_code.process(xml) client_protos_headers += wayland_scanner_client.process(xml) endforeach gdbus_codegen = find_program('gdbus-codegen') r = run_command(gdbus_codegen, '--body', '--output', '/dev/null') if r.returncode() != 0 gdbus_code_dsnw = custom_target( 'dbus-status-notifier-watcher.[ch]', output: ['@BASENAME@.c','@BASENAME@.h'], input: './dbus-status-notifier-watcher.xml', command: [gdbus_codegen,'--c-namespace', 'Sn', '--generate-c-code', 'protocol/@BASENAME@', '@INPUT@'], ) gdbus_code_dsni = custom_target( 'dbus-status-notifier-item.[ch]', output: ['@BASENAME@.c','@BASENAME@.h'], input: './dbus-status-notifier-item.xml', command: [gdbus_codegen,'--c-namespace', 'Sn', '--generate-c-code', 'protocol/@BASENAME@', '@INPUT@'], ) gdbus_code_dm = custom_target( 'dbus-menu.[ch]', output: ['@BASENAME@.c','@BASENAME@.h'], input: './dbus-menu.xml', command: [gdbus_codegen,'--c-namespace', 'Sn', '--generate-c-code', 'protocol/@BASENAME@', '@INPUT@'], ) client_protos_src += gdbus_code_dsnw[0] client_protos_headers += gdbus_code_dsnw[1] client_protos_src += gdbus_code_dsni[0] client_protos_headers += gdbus_code_dsni[1] client_protos_src += gdbus_code_dm[0] client_protos_headers += gdbus_code_dm[1] else gdbus_code = generator( gdbus_codegen, output: '@BASENAME@.c', arguments: ['--c-namespace', 'Sn', '--body', '--output', '@OUTPUT@', '@INPUT@'] ) gdbus_header = generator( gdbus_codegen, output: '@BASENAME@.h', arguments: ['--c-namespace', 'Sn', '--header', '--output', '@OUTPUT@', '@INPUT@'] ) client_protos_src += gdbus_code.process('./dbus-status-notifier-watcher.xml') client_protos_headers += gdbus_header.process('./dbus-status-notifier-watcher.xml') client_protos_src += gdbus_code.process('./dbus-status-notifier-item.xml') client_protos_headers += gdbus_header.process('./dbus-status-notifier-item.xml') client_protos_src += gdbus_code.process('./dbus-menu.xml') client_protos_headers += gdbus_header.process('./dbus-menu.xml') endif lib_client_protos = static_library( 'client_protos', client_protos_src + client_protos_headers, dependencies: [wayland_client, gtkmm, giounix], include_directories: include_directories('..'), ) # for the include directory client_protos = declare_dependency( link_with: lib_client_protos, sources: client_protos_headers, ) waybar-0.9.0/protocol/wlr-layer-shell-unstable-v1.xml000066400000000000000000000320611360163675500225700ustar00rootroot00000000000000 Copyright © 2017 Drew DeVault Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Clients can use this interface to assign the surface_layer role to wl_surfaces. Such surfaces are assigned to a "layer" of the output and rendered with a defined z-depth respective to each other. They may also be anchored to the edges and corners of a screen and specify input handling semantics. This interface should be suitable for the implementation of many desktop shell components, and a broad number of other applications that interact with the desktop. Create a layer surface for an existing surface. This assigns the role of layer_surface, or raises a protocol error if another role is already assigned. Creating a layer surface from a wl_surface which has a buffer attached or committed is a client error, and any attempts by a client to attach or manipulate a buffer prior to the first layer_surface.configure call must also be treated as errors. You may pass NULL for output to allow the compositor to decide which output to use. Generally this will be the one that the user most recently interacted with. Clients can specify a namespace that defines the purpose of the layer surface. These values indicate which layers a surface can be rendered in. They are ordered by z depth, bottom-most first. Traditional shell surfaces will typically be rendered between the bottom and top layers. Fullscreen shell surfaces are typically rendered at the top layer. Multiple surfaces can share a single layer, and ordering within a single layer is undefined. An interface that may be implemented by a wl_surface, for surfaces that are designed to be rendered as a layer of a stacked desktop-like environment. Layer surface state (size, anchor, exclusive zone, margin, interactivity) is double-buffered, and will be applied at the time wl_surface.commit of the corresponding wl_surface is called. Sets the size of the surface in surface-local coordinates. The compositor will display the surface centered with respect to its anchors. If you pass 0 for either value, the compositor will assign it and inform you of the assignment in the configure event. You must set your anchor to opposite edges in the dimensions you omit; not doing so is a protocol error. Both values are 0 by default. Size is double-buffered, see wl_surface.commit. Requests that the compositor anchor the surface to the specified edges and corners. If two orthoginal edges are specified (e.g. 'top' and 'left'), then the anchor point will be the intersection of the edges (e.g. the top left corner of the output); otherwise the anchor point will be centered on that edge, or in the center if none is specified. Anchor is double-buffered, see wl_surface.commit. Requests that the compositor avoids occluding an area of the surface with other surfaces. The compositor's use of this information is implementation-dependent - do not assume that this region will not actually be occluded. A positive value is only meaningful if the surface is anchored to an edge, rather than a corner. The zone is the number of surface-local coordinates from the edge that are considered exclusive. Surfaces that do not wish to have an exclusive zone may instead specify how they should interact with surfaces that do. If set to zero, the surface indicates that it would like to be moved to avoid occluding surfaces with a positive excluzive zone. If set to -1, the surface indicates that it would not like to be moved to accomodate for other surfaces, and the compositor should extend it all the way to the edges it is anchored to. For example, a panel might set its exclusive zone to 10, so that maximized shell surfaces are not shown on top of it. A notification might set its exclusive zone to 0, so that it is moved to avoid occluding the panel, but shell surfaces are shown underneath it. A wallpaper or lock screen might set their exclusive zone to -1, so that they stretch below or over the panel. The default value is 0. Exclusive zone is double-buffered, see wl_surface.commit. Requests that the surface be placed some distance away from the anchor point on the output, in surface-local coordinates. Setting this value for edges you are not anchored to has no effect. The exclusive zone includes the margin. Margin is double-buffered, see wl_surface.commit. Set to 1 to request that the seat send keyboard events to this layer surface. For layers below the shell surface layer, the seat will use normal focus semantics. For layers above the shell surface layers, the seat will always give exclusive keyboard focus to the top-most layer which has keyboard interactivity set to true. Layer surfaces receive pointer, touch, and tablet events normally. If you do not want to receive them, set the input region on your surface to an empty region. Events is double-buffered, see wl_surface.commit. This assigns an xdg_popup's parent to this layer_surface. This popup should have been created via xdg_surface::get_popup with the parent set to NULL, and this request must be invoked before committing the popup's initial state. See the documentation of xdg_popup for more details about what an xdg_popup is and how it is used. When a configure event is received, if a client commits the surface in response to the configure event, then the client must make an ack_configure request sometime before the commit request, passing along the serial of the configure event. If the client receives multiple configure events before it can respond to one, it only has to ack the last configure event. A client is not required to commit immediately after sending an ack_configure request - it may even ack_configure several times before its next surface commit. A client may send multiple ack_configure requests before committing, but only the last request sent before a commit indicates which configure event the client really is responding to. This request destroys the layer surface. The configure event asks the client to resize its surface. Clients should arrange their surface for the new states, and then send an ack_configure request with the serial sent in this configure event at some point before committing the new surface. The client is free to dismiss all but the last configure event it received. The width and height arguments specify the size of the window in surface-local coordinates. The size is a hint, in the sense that the client is free to ignore it if it doesn't resize, pick a smaller size (to satisfy aspect ratio or resize in steps of NxM pixels). If the client picks a smaller size and is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the surface will be centered on this axis. If the width or height arguments are zero, it means the client should decide its own window dimension. The closed event is sent by the compositor when the surface will no longer be shown. The output may have been destroyed or the user may have asked for it to be removed. Further changes to the surface will be ignored. The client should destroy the resource after receiving this event, and create a new surface if they so choose. waybar-0.9.0/resources/000077500000000000000000000000001360163675500150335ustar00rootroot00000000000000waybar-0.9.0/resources/config000066400000000000000000000114211360163675500162220ustar00rootroot00000000000000{ "layer": "top", // Waybar at top layer // "position": "bottom", // Waybar position (top|bottom|left|right) "height": 30, // Waybar height (to be removed for auto height) // "width": 1280, // Waybar width // Choose the order of the modules "modules-left": ["sway/workspaces", "sway/mode", "custom/media"], "modules-center": ["sway/window"], "modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "battery", "battery#bat2", "clock", "tray"], // Modules configuration // "sway/workspaces": { // "disable-scroll": true, // "all-outputs": true, // "format": "{name}: {icon}", // "format-icons": { // "1": "", // "2": "", // "3": "", // "4": "", // "5": "", // "urgent": "", // "focused": "", // "default": "" // } // }, "sway/mode": { "format": "{}" }, "mpd": { "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ", "format-disconnected": "Disconnected ", "format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ", "unknown-tag": "N/A", "interval": 2, "consume-icons": { "on": " " }, "random-icons": { "off": " ", "on": " " }, "repeat-icons": { "on": " " }, "single-icons": { "on": "1 " }, "state-icons": { "paused": "", "playing": "" }, "tooltip-format": "MPD (connected)", "tooltip-format-disconnected": "MPD (disconnected)" }, "idle_inhibitor": { "format": "{icon}", "format-icons": { "activated": "", "deactivated": "" } }, "tray": { // "icon-size": 21, "spacing": 10 }, "clock": { "tooltip-format": "{:%Y-%m-%d | %H:%M}", "format-alt": "{:%Y-%m-%d}" }, "cpu": { "format": "{usage}% ", "tooltip": false }, "memory": { "format": "{}% " }, "temperature": { // "thermal-zone": 2, // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", "critical-threshold": 80, // "format-critical": "{temperatureC}°C {icon}", "format": "{temperatureC}°C {icon}", "format-icons": ["", "", ""] }, "backlight": { // "device": "acpi_video1", "format": "{percent}% {icon}", "format-icons": ["", ""] }, "battery": { "states": { // "good": 95, "warning": 30, "critical": 15 }, "format": "{capacity}% {icon}", "format-charging": "{capacity}% ", "format-plugged": "{capacity}% ", "format-alt": "{time} {icon}", // "format-good": "", // An empty format will hide the module // "format-full": "", "format-icons": ["", "", "", "", ""] }, "battery#bat2": { "bat": "BAT2" }, "network": { // "interface": "wlp2*", // (Optional) To force the use of this interface "format-wifi": "{essid} ({signalStrength}%) ", "format-ethernet": "{ifname}: {ipaddr}/{cidr} ", "format-linked": "{ifname} (No IP) ", "format-disconnected": "Disconnected ⚠", "format-alt": "{ifname}: {ipaddr}/{cidr}" }, "pulseaudio": { // "scroll-step": 1, // %, can be a float "format": "{volume}% {icon} {format_source}", "format-bluetooth": "{volume}% {icon} {format_source}", "format-bluetooth-muted": " {icon} {format_source}", "format-muted": " {format_source}", "format-source": "{volume}% ", "format-source-muted": "", "format-icons": { "headphones": "", "handsfree": "", "headset": "", "phone": "", "portable": "", "car": "", "default": ["", "", ""] }, "on-click": "pavucontrol" }, "custom/media": { "format": "{icon} {}", "return-type": "json", "max-length": 40, "format-icons": { "spotify": "", "default": "🎜" }, "escape": true, "exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name } } waybar-0.9.0/resources/custom_modules/000077500000000000000000000000001360163675500200755ustar00rootroot00000000000000waybar-0.9.0/resources/custom_modules/mediaplayer.py000077500000000000000000000072521360163675500227540ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse import logging import sys import signal import gi import json gi.require_version('Playerctl', '2.0') from gi.repository import Playerctl, GLib logger = logging.getLogger(__name__) def write_output(text, player): logger.info('Writing output') output = {'text': text, 'class': 'custom-' + player.props.player_name, 'alt': player.props.player_name} sys.stdout.write(json.dumps(output) + '\n') sys.stdout.flush() def on_play(player, status, manager): logger.info('Received new playback status') on_metadata(player, player.props.metadata, manager) def on_metadata(player, metadata, manager): logger.info('Received new metadata') track_info = '' if player.props.player_name == 'spotify' and \ 'mpris:trackid' in metadata.keys() and \ ':ad:' in player.props.metadata['mpris:trackid']: track_info = 'AD PLAYING' elif player.get_artist() != '' and player.get_title() != '': track_info = '{artist} - {title}'.format(artist=player.get_artist(), title=player.get_title()) if player.props.status != 'Playing' and track_info: track_info = ' ' + track_info write_output(track_info, player) def on_player_appeared(manager, player, selected_player=None): if player is not None and (selected_player is None or player.name == selected_player): init_player(manager, player) else: logger.debug("New player appeared, but it's not the selected player, skipping") def on_player_vanished(manager, player): logger.info('Player has vanished') sys.stdout.write('\n') sys.stdout.flush() def init_player(manager, name): logger.debug('Initialize player: {player}'.format(player=name.name)) player = Playerctl.Player.new_from_name(name) player.connect('playback-status', on_play, manager) player.connect('metadata', on_metadata, manager) manager.manage_player(player) on_metadata(player, player.props.metadata, manager) def signal_handler(sig, frame): logger.debug('Received signal to stop, exiting') sys.stdout.write('\n') sys.stdout.flush() # loop.quit() sys.exit(0) def parse_arguments(): parser = argparse.ArgumentParser() # Increase verbosity with every occurance of -v parser.add_argument('-v', '--verbose', action='count', default=0) # Define for which player we're listening parser.add_argument('--player') return parser.parse_args() def main(): arguments = parse_arguments() # Initialize logging logging.basicConfig(stream=sys.stderr, level=logging.DEBUG, format='%(name)s %(levelname)s %(message)s') # Logging is set by default to WARN and higher. # With every occurrence of -v it's lowered by one logger.setLevel(max((3 - arguments.verbose) * 10, 0)) # Log the sent command line arguments logger.debug('Arguments received {}'.format(vars(arguments))) manager = Playerctl.PlayerManager() loop = GLib.MainLoop() manager.connect('name-appeared', lambda *args: on_player_appeared(*args, arguments.player)) manager.connect('player-vanished', on_player_vanished) signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) for player in manager.props.player_names: if arguments.player is not None and arguments.player != player.name: logger.debug('{player} is not the filtered player, skipping it' .format(player=player.name) ) continue init_player(manager, player) loop.run() if __name__ == '__main__': main() waybar-0.9.0/resources/style.css000066400000000000000000000062331360163675500167110ustar00rootroot00000000000000* { border: none; border-radius: 0; /* `otf-font-awesome` is required to be installed for icons */ font-family: Roboto, Helvetica, Arial, sans-serif; font-size: 13px; min-height: 0; } window#waybar { background-color: rgba(43, 48, 59, 0.5); border-bottom: 3px solid rgba(100, 114, 125, 0.5); color: #ffffff; transition-property: background-color; transition-duration: .5s; } window#waybar.hidden { opacity: 0.2; } /* window#waybar.empty { background-color: transparent; } window#waybar.solo { background-color: #FFFFFF; } */ window#waybar.termite { background-color: #3F3F3F; } window#waybar.chromium { background-color: #000000; border: none; } #workspaces button { padding: 0 5px; background-color: transparent; color: #ffffff; border-bottom: 3px solid transparent; } /* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ #workspaces button:hover { background: rgba(0, 0, 0, 0.2); box-shadow: inherit; border-bottom: 3px solid #ffffff; } #workspaces button.focused { background-color: #64727D; border-bottom: 3px solid #ffffff; } #workspaces button.urgent { background-color: #eb4d4b; } #mode { background-color: #64727D; border-bottom: 3px solid #ffffff; } #clock, #battery, #cpu, #memory, #temperature, #backlight, #network, #pulseaudio, #custom-media, #tray, #mode, #idle_inhibitor, #mpd { padding: 0 10px; margin: 0 4px; color: #ffffff; } #clock { background-color: #64727D; } #battery { background-color: #ffffff; color: #000000; } #battery.charging { color: #ffffff; background-color: #26A65B; } @keyframes blink { to { background-color: #ffffff; color: #000000; } } #battery.critical:not(.charging) { background-color: #f53c3c; color: #ffffff; animation-name: blink; animation-duration: 0.5s; animation-timing-function: linear; animation-iteration-count: infinite; animation-direction: alternate; } label:focus { background-color: #000000; } #cpu { background-color: #2ecc71; color: #000000; } #memory { background-color: #9b59b6; } #backlight { background-color: #90b1b1; } #network { background-color: #2980b9; } #network.disconnected { background-color: #f53c3c; } #pulseaudio { background-color: #f1c40f; color: #000000; } #pulseaudio.muted { background-color: #90b1b1; color: #2a5c45; } #custom-media { background-color: #66cc99; color: #2a5c45; min-width: 100px; } #custom-media.custom-spotify { background-color: #66cc99; } #custom-media.custom-vlc { background-color: #ffa000; } #temperature { background-color: #f0932b; } #temperature.critical { background-color: #eb4d4b; } #tray { background-color: #2980b9; } #idle_inhibitor { background-color: #2d3436; } #idle_inhibitor.activated { background-color: #ecf0f1; color: #2d3436; } #mpd { background-color: #66cc99; color: #2a5c45; } #mpd.disconnected { background-color: #f53c3c; } #mpd.stopped { background-color: #90b1b1; } #mpd.paused { background-color: #51a37a; } waybar-0.9.0/resources/waybar.service.in000066400000000000000000000004521360163675500203100ustar00rootroot00000000000000[Unit] Description=Highly customizable Wayland bar for Sway and Wlroots based compositors. Documentation=https://github.com/Alexays/Waybar/wiki/ PartOf=wayland-session.target [Service] Type=dbus BusName=fr.arouillard.waybar ExecStart=@prefix@/bin/waybar [Install] WantedBy=wayland-session.target waybar-0.9.0/src/000077500000000000000000000000001360163675500136105ustar00rootroot00000000000000waybar-0.9.0/src/ALabel.cpp000066400000000000000000000063071360163675500154420ustar00rootroot00000000000000#include "ALabel.hpp" #include #include namespace waybar { ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id, const std::string& format, uint16_t interval, bool ellipsize) : AModule(config, name, id, config["format-alt"].isString()), format_(config_["format"].isString() ? config_["format"].asString() : format), interval_(config_["interval"] == "once" ? std::chrono::seconds(100000000) : std::chrono::seconds( config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)), default_format_(format_) { label_.set_name(name); if (!id.empty()) { label_.get_style_context()->add_class(id); } event_box_.add(label_); if (config_["max-length"].isUInt()) { label_.set_max_width_chars(config_["max-length"].asUInt()); label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); } else if (ellipsize && label_.get_max_width_chars() == -1) { label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); } if (config_["rotate"].isUInt()) { label_.set_angle(config["rotate"].asUInt()); } } auto ALabel::update() -> void { // Nothing here } std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) { auto format_icons = config_["format-icons"]; if (format_icons.isObject()) { if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) { format_icons = format_icons[alt]; } else { format_icons = format_icons["default"]; } } if (format_icons.isArray()) { auto size = format_icons.size(); auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); format_icons = format_icons[idx]; } if (format_icons.isString()) { return format_icons.asString(); } return ""; } bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) { alt_ = !alt_; if (alt_ && config_["format-alt"].isString()) { format_ = config_["format-alt"].asString(); } else { format_ = default_format_; } } return AModule::handleToggle(e); } std::string ALabel::getState(uint8_t value, bool lesser) { if (!config_["states"].isObject()) { return ""; } // Get current state std::vector> states; if (config_["states"].isObject()) { for (auto it = config_["states"].begin(); it != config_["states"].end(); ++it) { if (it->isUInt() && it.key().isString()) { states.emplace_back(it.key().asString(), it->asUInt()); } } } // Sort states std::sort(states.begin(), states.end(), [&lesser](auto& a, auto& b) { return lesser ? a.second < b.second : a.second > b.second; }); std::string valid_state; for (auto const& state : states) { if ((lesser ? value <= state.second : value >= state.second) && valid_state.empty()) { label_.get_style_context()->add_class(state.first); valid_state = state.first; } else { label_.get_style_context()->remove_class(state.first); } } return valid_state; } } // namespace waybar waybar-0.9.0/src/AModule.cpp000066400000000000000000000075211360163675500156470ustar00rootroot00000000000000#include "AModule.hpp" #include #include namespace waybar { AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id, bool enable_click, bool enable_scroll) : config_(std::move(config)) { // configure events' user commands if (config_["on-click"].isString() || config_["on-click-middle"].isString() || config_["on-click-backward"].isString() || config_["on-click-forward"].isString() || config_["on-click-right"].isString() || enable_click) { event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); } if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || enable_scroll) { event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll)); } } AModule::~AModule() { for (const auto& pid : pid_) { if (pid != -1) { kill(-pid, 9); } } } auto AModule::update() -> void { // Nothing here } bool AModule::handleToggle(GdkEventButton* const& e) { std::string format; if (config_["on-click"].isString() && e->button == 1) { format = config_["on-click"].asString(); } else if (config_["on-click-middle"].isString() && e->button == 2) { format = config_["on-click-middle"].asString(); } else if (config_["on-click-right"].isString() && e->button == 3) { format = config_["on-click-right"].asString(); } else if (config_["on-click-forward"].isString() && e->button == 8) { format = config_["on-click-backward"].asString(); } else if (config_["on-click-backward"].isString() && e->button == 9) { format = config_["on-click-forward"].asString(); } if (!format.empty()) { pid_.push_back(util::command::forkExec(format)); } dp.emit(); return true; } AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { switch (e -> direction) { case GDK_SCROLL_UP: return SCROLL_DIR::UP; case GDK_SCROLL_DOWN: return SCROLL_DIR::DOWN; case GDK_SCROLL_LEFT: return SCROLL_DIR::LEFT; case GDK_SCROLL_RIGHT: return SCROLL_DIR::RIGHT; case GDK_SCROLL_SMOOTH: { SCROLL_DIR dir{SCROLL_DIR::NONE}; distance_scrolled_y_ += e->delta_y; distance_scrolled_x_ += e->delta_x; gdouble threshold = 0; if (config_["smooth-scrolling-threshold"].isNumeric()) { threshold = config_["smooth-scrolling-threshold"].asDouble(); } if (distance_scrolled_y_ < -threshold) { dir = SCROLL_DIR::UP; } else if (distance_scrolled_y_ > threshold) { dir = SCROLL_DIR::DOWN; } else if (distance_scrolled_x_ > threshold) { dir = SCROLL_DIR::RIGHT; } else if (distance_scrolled_x_ < -threshold) { dir = SCROLL_DIR::LEFT; } switch (dir) { case SCROLL_DIR::UP: case SCROLL_DIR::DOWN: distance_scrolled_y_ = 0; break; case SCROLL_DIR::LEFT: case SCROLL_DIR::RIGHT: distance_scrolled_x_ = 0; break; case SCROLL_DIR::NONE: break; } return dir; } // Silence -Wreturn-type: default: return SCROLL_DIR::NONE; } } bool AModule::handleScroll(GdkEventScroll* e) { auto dir = getScrollDir(e); if (dir == SCROLL_DIR::UP && config_["on-scroll-up"].isString()) { pid_.push_back(util::command::forkExec(config_["on-scroll-up"].asString())); } else if (dir == SCROLL_DIR::DOWN && config_["on-scroll-down"].isString()) { pid_.push_back(util::command::forkExec(config_["on-scroll-down"].asString())); } dp.emit(); return true; } bool AModule::tooltipEnabled() { return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true; } AModule::operator Gtk::Widget&() { return event_box_; } } // namespace waybar waybar-0.9.0/src/bar.cpp000066400000000000000000000354321360163675500150670ustar00rootroot00000000000000#ifdef HAVE_GTK_LAYER_SHELL #include #endif #include "bar.hpp" #include "client.hpp" #include "factory.hpp" #include waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) : output(w_output), config(w_config), window{Gtk::WindowType::WINDOW_TOPLEVEL}, surface(nullptr), layer_surface_(nullptr), anchor_(ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP), left_(Gtk::ORIENTATION_HORIZONTAL, 0), center_(Gtk::ORIENTATION_HORIZONTAL, 0), right_(Gtk::ORIENTATION_HORIZONTAL, 0), box_(Gtk::ORIENTATION_HORIZONTAL, 0) { window.set_title("waybar"); window.set_name("waybar"); window.set_decorated(false); window.get_style_context()->add_class(output->name); window.get_style_context()->add_class(config["name"].asString()); window.get_style_context()->add_class(config["position"].asString()); if (config["position"] == "right" || config["position"] == "left") { height_ = 0; width_ = 1; } height_ = config["height"].isUInt() ? config["height"].asUInt() : height_; width_ = config["width"].isUInt() ? config["width"].asUInt() : width_; if (config["position"] == "bottom") { anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; } else if (config["position"] == "left") { anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; } else if (config["position"] == "right") { anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; } if (anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM || anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { anchor_ |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; } else if (anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT || anchor_ == ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { anchor_ |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; left_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); box_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); vertical = true; } if (config["margin-top"].isInt() || config["margin-right"].isInt() || config["margin-bottom"].isInt() || config["margin-left"].isInt()) { margins_ = { config["margin-top"].isInt() ? config["margin-top"].asInt() : 0, config["margin-right"].isInt() ? config["margin-right"].asInt() : 0, config["margin-bottom"].isInt() ? config["margin-bottom"].asInt() : 0, config["margin-left"].isInt() ? config["margin-left"].asInt() : 0, }; } else if (config["margin"].isString()) { std::istringstream iss(config["margin"].asString()); std::vector margins{std::istream_iterator(iss), {}}; try { if (margins.size() == 1) { auto gaps = std::stoi(margins[0], nullptr, 10); margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps}; } if (margins.size() == 2) { auto vertical_margins = std::stoi(margins[0], nullptr, 10); auto horizontal_margins = std::stoi(margins[1], nullptr, 10); margins_ = {.top = vertical_margins, .right = horizontal_margins, .bottom = vertical_margins, .left = horizontal_margins}; } if (margins.size() == 3) { auto horizontal_margins = std::stoi(margins[1], nullptr, 10); margins_ = {.top = std::stoi(margins[0], nullptr, 10), .right = horizontal_margins, .bottom = std::stoi(margins[2], nullptr, 10), .left = horizontal_margins}; } if (margins.size() == 4) { margins_ = {.top = std::stoi(margins[0], nullptr, 10), .right = std::stoi(margins[1], nullptr, 10), .bottom = std::stoi(margins[2], nullptr, 10), .left = std::stoi(margins[3], nullptr, 10)}; } } catch (...) { spdlog::warn("Invalid margins: {}", config["margin"].asString()); } } else if (config["margin"].isInt()) { auto gaps = config["margin"].asInt(); margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps}; } #ifdef HAVE_GTK_LAYER_SHELL use_gls_ = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true; if (use_gls_) { initGtkLayerShell(); } #endif window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize)); window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap)); window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure)); window.set_size_request(width_, height_); setupWidgets(); if (window.get_realized()) { onRealize(); } window.show_all(); } void waybar::Bar::onConfigure(GdkEventConfigure* ev) { auto tmp_height = height_; auto tmp_width = width_; if (ev->height > static_cast(height_)) { // Default minimal value if (height_ != 1) { spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); } if (config["height"].isUInt()) { spdlog::info(SIZE_DEFINED, "Height"); } else { tmp_height = ev->height; } } if (ev->width > static_cast(width_)) { // Default minimal value if (width_ != 1) { spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); } if (config["width"].isUInt()) { spdlog::info(SIZE_DEFINED, "Width"); } else { tmp_width = ev->width; } } if (use_gls_) { width_ = tmp_width; height_ = tmp_height; spdlog::debug("Set surface size {}x{} for output {}", width_, height_, output->name); setExclusiveZone(tmp_width, tmp_height); } else if (tmp_width != width_ || tmp_height != height_) { setSurfaceSize(tmp_width, tmp_height); } } #ifdef HAVE_GTK_LAYER_SHELL void waybar::Bar::initGtkLayerShell() { auto gtk_window = window.gobj(); // this has to be executed before GtkWindow.realize gtk_layer_init_for_window(gtk_window); gtk_layer_set_keyboard_interactivity(gtk_window, FALSE); auto layer = config["layer"] == "top" ? GTK_LAYER_SHELL_LAYER_TOP : GTK_LAYER_SHELL_LAYER_BOTTOM; gtk_layer_set_layer(gtk_window, layer); gtk_layer_set_monitor(gtk_window, output->monitor->gobj()); gtk_layer_set_namespace(gtk_window, "waybar"); gtk_layer_set_anchor( gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); gtk_layer_set_anchor( gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT); gtk_layer_set_anchor( gtk_window, GTK_LAYER_SHELL_EDGE_TOP, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP); gtk_layer_set_anchor( gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM); gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, margins_.left); gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right); gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_TOP, margins_.top); gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, margins_.bottom); } #endif void waybar::Bar::onRealize() { auto gdk_window = window.get_window()->gobj(); gdk_wayland_window_set_use_custom_surface(gdk_window); } void waybar::Bar::onMap(GdkEventAny* ev) { auto gdk_window = window.get_window()->gobj(); surface = gdk_wayland_window_get_wl_surface(gdk_window); if (use_gls_) { return; } auto client = waybar::Client::inst(); // owned by output->monitor; no need to destroy auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj()); auto layer = config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; layer_surface_ = zwlr_layer_shell_v1_get_layer_surface( client->layer_shell, surface, wl_output, layer, "waybar"); zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_, false); zwlr_layer_surface_v1_set_anchor(layer_surface_, anchor_); zwlr_layer_surface_v1_set_margin( layer_surface_, margins_.top, margins_.right, margins_.bottom, margins_.left); setSurfaceSize(width_, height_); setExclusiveZone(width_, height_); static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { .configure = layerSurfaceHandleConfigure, .closed = layerSurfaceHandleClosed, }; zwlr_layer_surface_v1_add_listener(layer_surface_, &layer_surface_listener, this); wl_surface_commit(surface); wl_display_roundtrip(client->wl_display); } void waybar::Bar::setExclusiveZone(uint32_t width, uint32_t height) { auto zone = 0; if (visible) { // exclusive zone already includes margin for anchored edge, // only opposite margin should be added if (vertical) { zone += width; zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) ? margins_.right : margins_.left; } else { zone += height; zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) ? margins_.bottom : margins_.top; } } spdlog::debug("Set exclusive zone {} for output {}", zone, output->name); #ifdef HAVE_GTK_LAYER_SHELL if (use_gls_) { gtk_layer_set_exclusive_zone(window.gobj(), zone); } else #endif { zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_, zone); } } void waybar::Bar::setSurfaceSize(uint32_t width, uint32_t height) { /* If the client is anchored to two opposite edges, layer_surface.configure will return * size without margins for the axis. * layer_surface.set_size, however, expects size with margins for the anchored axis. * This is not specified by wlr-layer-shell and based on actual behavior of sway. */ if (vertical && height > 1) { height += margins_.top + margins_.bottom; } if (!vertical && width > 1) { width += margins_.right + margins_.left; } spdlog::debug("Set surface size {}x{} for output {}", width, height, output->name); zwlr_layer_surface_v1_set_size(layer_surface_, width, height); } // Converting string to button code rn as to avoid doing it later void waybar::Bar::setupAltFormatKeyForModule(const std::string& module_name) { if (config.isMember(module_name)) { Json::Value& module = config[module_name]; if (module.isMember("format-alt")) { if (module.isMember("format-alt-click")) { Json::Value& click = module["format-alt-click"]; if (click.isString()) { if (click == "click-right") { module["format-alt-click"] = 3U; } else if (click == "click-middle") { module["format-alt-click"] = 2U; } else if (click == "click-backward") { module["format-alt-click"] = 8U; } else if (click == "click-forward") { module["format-alt-click"] = 9U; } else { module["format-alt-click"] = 1U; // default click-left } } else { module["format-alt-click"] = 1U; } } else { module["format-alt-click"] = 1U; } } } } void waybar::Bar::setupAltFormatKeyForModuleList(const char* module_list_name) { if (config.isMember(module_list_name)) { Json::Value& modules = config[module_list_name]; for (const Json::Value& module_name : modules) { if (module_name.isString()) { setupAltFormatKeyForModule(module_name.asString()); } } } } void waybar::Bar::handleSignal(int signal) { for (auto& module : modules_left_) { auto* custom = dynamic_cast(module.get()); if (custom != nullptr) { custom->refresh(signal); } } for (auto& module : modules_center_) { auto* custom = dynamic_cast(module.get()); if (custom != nullptr) { custom->refresh(signal); } } for (auto& module : modules_right_) { auto* custom = dynamic_cast(module.get()); if (custom != nullptr) { custom->refresh(signal); } } } void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surface_v1* surface, uint32_t serial, uint32_t width, uint32_t height) { auto o = static_cast(data); if (width != o->width_ || height != o->height_) { o->width_ = width; o->height_ = height; o->window.set_size_request(o->width_, o->height_); o->window.resize(o->width_, o->height_); o->setExclusiveZone(width, height); spdlog::info(BAR_SIZE_MSG, o->width_ == 1 ? "auto" : std::to_string(o->width_), o->height_ == 1 ? "auto" : std::to_string(o->height_), o->output->name); wl_surface_commit(o->surface); } zwlr_layer_surface_v1_ack_configure(surface, serial); } void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface_v1* /*surface*/) { auto o = static_cast(data); if (o->layer_surface_) { zwlr_layer_surface_v1_destroy(o->layer_surface_); o->layer_surface_ = nullptr; } o->modules_left_.clear(); o->modules_center_.clear(); o->modules_right_.clear(); } auto waybar::Bar::toggle() -> void { visible = !visible; if (!visible) { window.get_style_context()->add_class("hidden"); window.set_opacity(0); } else { window.get_style_context()->remove_class("hidden"); window.set_opacity(1); } setExclusiveZone(width_, height_); wl_surface_commit(surface); } void waybar::Bar::getModules(const Factory& factory, const std::string& pos) { if (config[pos].isArray()) { for (const auto& name : config[pos]) { try { auto module = factory.makeModule(name.asString()); if (pos == "modules-left") { modules_left_.emplace_back(module); } if (pos == "modules-center") { modules_center_.emplace_back(module); } if (pos == "modules-right") { modules_right_.emplace_back(module); } module->dp.connect([module, &name] { try { module->update(); } catch (const std::exception& e) { spdlog::error("{}: {}", name.asString(), e.what()); } }); } catch (const std::exception& e) { spdlog::warn("module {}: {}", name.asString(), e.what()); } } } } auto waybar::Bar::setupWidgets() -> void { window.add(box_); box_.pack_start(left_, false, false); box_.set_center_widget(center_); box_.pack_end(right_, false, false); // Convert to button code for every module that is used. setupAltFormatKeyForModuleList("modules-left"); setupAltFormatKeyForModuleList("modules-right"); setupAltFormatKeyForModuleList("modules-center"); Factory factory(*this, config); getModules(factory, "modules-left"); getModules(factory, "modules-center"); getModules(factory, "modules-right"); for (auto const& module : modules_left_) { left_.pack_start(*module, false, false); } for (auto const& module : modules_center_) { center_.pack_start(*module, false, false); } std::reverse(modules_right_.begin(), modules_right_.end()); for (auto const& module : modules_right_) { right_.pack_end(*module, false, false); } } waybar-0.9.0/src/client.cpp000066400000000000000000000247121360163675500156000ustar00rootroot00000000000000#include "client.hpp" #include #include #include #include #include "util/clara.hpp" #include "util/json.hpp" waybar::Client *waybar::Client::inst() { static auto c = new Client(); return c; } const std::string waybar::Client::getValidPath(const std::vector &paths) const { wordexp_t p; for (const std::string &path : paths) { if (wordexp(path.c_str(), &p, 0) == 0) { if (access(*p.we_wordv, F_OK) == 0) { std::string result = *p.we_wordv; wordfree(&p); return result; } wordfree(&p); } } return std::string(); } void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { auto client = static_cast(data); if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { client->layer_shell = static_cast( wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version)); } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { client->xdg_output_manager = static_cast(wl_registry_bind( registry, name, &zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION)); } else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) { client->idle_inhibit_manager = static_cast( wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1)); } } void waybar::Client::handleGlobalRemove(void * data, struct wl_registry * /*registry*/, uint32_t name) { // Nothing here } void waybar::Client::handleOutput(std::unique_ptr &output) { static const struct zxdg_output_v1_listener xdgOutputListener = { .logical_position = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {}, .logical_size = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {}, .done = [](void *, struct zxdg_output_v1 *) {}, .name = &handleOutputName, .description = [](void *, struct zxdg_output_v1 *, const char *) {}, }; // owned by output->monitor; no need to destroy auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj()); output->xdg_output.reset(zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, wl_output)); zxdg_output_v1_add_listener(output->xdg_output.get(), &xdgOutputListener, output.get()); } bool waybar::Client::isValidOutput(const Json::Value & config, std::unique_ptr &output) { bool found = true; if (config["output"].isArray()) { bool in_array = false; for (auto const &output_conf : config["output"]) { if (output_conf.isString() && output_conf.asString() == output->name) { in_array = true; break; } } found = in_array; } if (config["output"].isString() && config["output"].asString() != output->name) { found = false; } return found; } std::unique_ptr &waybar::Client::getOutput(void *addr) { auto it = std::find_if(outputs_.begin(), outputs_.end(), [&addr](const auto &output) { return output.get() == addr; }); if (it == outputs_.end()) { throw std::runtime_error("Unable to find valid output"); } return *it; } std::vector waybar::Client::getOutputConfigs( std::unique_ptr &output) { std::vector configs; if (config_.isArray()) { for (auto const &config : config_) { if (config.isObject() && isValidOutput(config, output)) { configs.push_back(config); } } } else if (isValidOutput(config_, output)) { configs.push_back(config_); } return configs; } void waybar::Client::handleOutputName(void * data, struct zxdg_output_v1 * /*xdg_output*/, const char *name) { auto client = waybar::Client::inst(); try { auto &output = client->getOutput(data); output->name = name; spdlog::debug("Output detected: {} ({} {})", name, output->monitor->get_manufacturer(), output->monitor->get_model()); auto configs = client->getOutputConfigs(output); if (configs.empty()) { output->xdg_output.reset(); } else { wl_display_roundtrip(client->wl_display); for (const auto &config : configs) { client->bars.emplace_back(std::make_unique(output.get(), config)); Glib::RefPtr screen = client->bars.back()->window.get_screen(); client->style_context_->add_provider_for_screen( screen, client->css_provider_, GTK_STYLE_PROVIDER_PRIORITY_USER); } } } catch (const std::exception &e) { std::cerr << e.what() << std::endl; } } void waybar::Client::handleMonitorAdded(Glib::RefPtr monitor) { auto &output = outputs_.emplace_back(new struct waybar_output({monitor})); handleOutput(output); } void waybar::Client::handleMonitorRemoved(Glib::RefPtr monitor) { spdlog::debug("Output removed: {} {}", monitor->get_manufacturer(), monitor->get_model()); for (auto it = bars.begin(); it != bars.end();) { if ((*it)->output->monitor == monitor) { auto output_name = (*it)->output->name; (*it)->window.close(); it = bars.erase(it); spdlog::info("Bar removed from output: {}", output_name); } else { ++it; } } std::remove_if(outputs_.begin(), outputs_.end(), [&monitor](const auto &output) { return output->monitor == monitor; }); } std::tuple waybar::Client::getConfigs( const std::string &config, const std::string &style) const { auto config_file = config.empty() ? getValidPath({ "$XDG_CONFIG_HOME/waybar/config", "$HOME/.config/waybar/config", "$HOME/waybar/config", "/etc/xdg/waybar/config", "./resources/config", }) : config; auto css_file = style.empty() ? getValidPath({ "$XDG_CONFIG_HOME/waybar/style.css", "$HOME/.config/waybar/style.css", "$HOME/waybar/style.css", "/etc/xdg/waybar/style.css", "./resources/style.css", }) : style; if (css_file.empty() || config_file.empty()) { throw std::runtime_error("Missing required resources files"); } spdlog::info("Resources files: {}, {}", config_file, css_file); return {config_file, css_file}; } auto waybar::Client::setupConfig(const std::string &config_file) -> void { std::ifstream file(config_file); if (!file.is_open()) { throw std::runtime_error("Can't open config file"); } std::string str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); util::JsonParser parser; config_ = parser.parse(str); } auto waybar::Client::setupCss(const std::string &css_file) -> void { css_provider_ = Gtk::CssProvider::create(); style_context_ = Gtk::StyleContext::create(); // Load our css file, wherever that may be hiding if (!css_provider_->load_from_path(css_file)) { throw std::runtime_error("Can't open style file"); } } void waybar::Client::bindInterfaces() { registry = wl_display_get_registry(wl_display); static const struct wl_registry_listener registry_listener = { .global = handleGlobal, .global_remove = handleGlobalRemove, }; wl_registry_add_listener(registry, ®istry_listener, this); wl_display_roundtrip(wl_display); if (layer_shell == nullptr || xdg_output_manager == nullptr) { throw std::runtime_error("Failed to acquire required resources."); } // add existing outputs and subscribe to updates for (auto i = 0; i < gdk_display->get_n_monitors(); ++i) { auto monitor = gdk_display->get_monitor(i); handleMonitorAdded(monitor); } gdk_display->signal_monitor_added().connect(sigc::mem_fun(*this, &Client::handleMonitorAdded)); gdk_display->signal_monitor_removed().connect( sigc::mem_fun(*this, &Client::handleMonitorRemoved)); } int waybar::Client::main(int argc, char *argv[]) { bool show_help = false; bool show_version = false; std::string config; std::string style; std::string bar_id; std::string log_level; auto cli = clara::detail::Help(show_help) | clara::detail::Opt(show_version)["-v"]["--version"]("Show version") | clara::detail::Opt(config, "config")["-c"]["--config"]("Config path") | clara::detail::Opt(style, "style")["-s"]["--style"]("Style path") | clara::detail::Opt( log_level, "trace|debug|info|warning|error|critical|off")["-l"]["--log-level"]("Log level") | clara::detail::Opt(bar_id, "id")["-b"]["--bar"]("Bar id"); auto res = cli.parse(clara::detail::Args(argc, argv)); if (!res) { spdlog::error("Error in command line: {}", res.errorMessage()); return 1; } if (show_help) { std::cout << cli << std::endl; return 0; } if (show_version) { std::cout << "Waybar v" << VERSION << std::endl; return 0; } if (!log_level.empty()) { spdlog::set_level(spdlog::level::from_str(log_level)); } gtk_app = Gtk::Application::create(argc, argv, "fr.arouillard.waybar"); gdk_display = Gdk::Display::get_default(); if (!gdk_display) { throw std::runtime_error("Can't find display"); } if (!GDK_IS_WAYLAND_DISPLAY(gdk_display->gobj())) { throw std::runtime_error("Bar need to run under Wayland"); } wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj()); auto [config_file, css_file] = getConfigs(config, style); setupConfig(config_file); setupCss(css_file); bindInterfaces(); gtk_app->hold(); gtk_app->run(); bars.clear(); zxdg_output_manager_v1_destroy(xdg_output_manager); zwlr_layer_shell_v1_destroy(layer_shell); zwp_idle_inhibit_manager_v1_destroy(idle_inhibit_manager); wl_registry_destroy(registry); wl_display_disconnect(wl_display); return 0; } waybar-0.9.0/src/factory.cpp000066400000000000000000000050151360163675500157640ustar00rootroot00000000000000#include "factory.hpp" waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {} waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { try { auto hash_pos = name.find('#'); auto ref = name.substr(0, hash_pos); auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : ""; #ifndef NO_FILESYSTEM if (ref == "battery") { return new waybar::modules::Battery(id, config_[name]); } #endif #ifdef HAVE_SWAY if (ref == "sway/mode") { return new waybar::modules::sway::Mode(id, config_[name]); } if (ref == "sway/workspaces") { return new waybar::modules::sway::Workspaces(id, bar_, config_[name]); } if (ref == "sway/window") { return new waybar::modules::sway::Window(id, bar_, config_[name]); } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); } if (ref == "memory") { return new waybar::modules::Memory(id, config_[name]); } if (ref == "cpu") { return new waybar::modules::Cpu(id, config_[name]); } if (ref == "clock") { return new waybar::modules::Clock(id, config_[name]); } if (ref == "disk") { return new waybar::modules::Disk(id, config_[name]); } #if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM) if (ref == "tray") { return new waybar::modules::SNI::Tray(id, bar_, config_[name]); } #endif #ifdef HAVE_LIBNL if (ref == "network") { return new waybar::modules::Network(id, config_[name]); } #endif #ifdef HAVE_LIBUDEV if (ref == "backlight") { return new waybar::modules::Backlight(id, config_[name]); } #endif #ifdef HAVE_LIBPULSE if (ref == "pulseaudio") { return new waybar::modules::Pulseaudio(id, config_[name]); } #endif #ifdef HAVE_LIBMPDCLIENT if (ref == "mpd") { return new waybar::modules::MPD(id, config_[name]); } #endif if (ref == "temperature") { return new waybar::modules::Temperature(id, config_[name]); } if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { return new waybar::modules::Custom(ref.substr(7), id, config_[name]); } } catch (const std::exception& e) { auto err = fmt::format("Disabling module \"{}\", {}", name, e.what()); throw std::runtime_error(err); } catch (...) { auto err = fmt::format("Disabling module \"{}\", Unknown reason", name); throw std::runtime_error(err); } throw std::runtime_error("Unknown module: " + name); } waybar-0.9.0/src/main.cpp000066400000000000000000000014301360163675500152360ustar00rootroot00000000000000#include #include #include "client.hpp" int main(int argc, char* argv[]) { try { auto client = waybar::Client::inst(); std::signal(SIGUSR1, [](int /*signal*/) { for (auto& bar : waybar::Client::inst()->bars) { bar->toggle(); } }); for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) { std::signal(sig, [](int sig) { for (auto& bar : waybar::Client::inst()->bars) { bar->handleSignal(sig); } }); } auto ret = client->main(argc, argv); delete client; return ret; } catch (const std::exception& e) { spdlog::error("{}", e.what()); return 1; } catch (const Glib::Exception& e) { spdlog::error("{}", static_cast(e.what())); return 1; } } waybar-0.9.0/src/modules/000077500000000000000000000000001360163675500152605ustar00rootroot00000000000000waybar-0.9.0/src/modules/backlight.cpp000066400000000000000000000203711360163675500177170ustar00rootroot00000000000000#include "modules/backlight.hpp" #include #include #include #include #include #include #include namespace { class FileDescriptor { public: explicit FileDescriptor(int fd) : fd_(fd) {} FileDescriptor(const FileDescriptor &other) = delete; FileDescriptor(FileDescriptor &&other) noexcept = delete; FileDescriptor &operator=(const FileDescriptor &other) = delete; FileDescriptor &operator=(FileDescriptor &&other) noexcept = delete; ~FileDescriptor() { if (fd_ != -1) { if (close(fd_) != 0) { fmt::print(stderr, "Failed to close fd: {}\n", errno); } } } int get() const { return fd_; } private: int fd_; }; struct UdevDeleter { void operator()(udev *ptr) { udev_unref(ptr); } }; struct UdevDeviceDeleter { void operator()(udev_device *ptr) { udev_device_unref(ptr); } }; struct UdevEnumerateDeleter { void operator()(udev_enumerate *ptr) { udev_enumerate_unref(ptr); } }; struct UdevMonitorDeleter { void operator()(udev_monitor *ptr) { udev_monitor_unref(ptr); } }; void check_eq(int rc, int expected, const char *message = "eq, rc was: ") { if (rc != expected) { throw std::runtime_error(fmt::format(message, rc)); } } void check_neq(int rc, int bad_rc, const char *message = "neq, rc was: ") { if (rc == bad_rc) { throw std::runtime_error(fmt::format(message, rc)); } } void check0(int rc, const char *message = "rc wasn't 0") { check_eq(rc, 0, message); } void check_gte(int rc, int gte, const char *message = "rc was: ") { if (rc < gte) { throw std::runtime_error(fmt::format(message, rc)); } } void check_nn(const void *ptr, const char *message = "ptr was null") { if (ptr == nullptr) { throw std::runtime_error(message); } } } // namespace waybar::modules::Backlight::BacklightDev::BacklightDev(std::string name, int actual, int max) : name_(std::move(name)), actual_(actual), max_(max) {} std::string_view waybar::modules::Backlight::BacklightDev::name() const { return name_; } int waybar::modules::Backlight::BacklightDev::get_actual() const { return actual_; } void waybar::modules::Backlight::BacklightDev::set_actual(int actual) { actual_ = actual; } int waybar::modules::Backlight::BacklightDev::get_max() const { return max_; } void waybar::modules::Backlight::BacklightDev::set_max(int max) { max_ = max; } waybar::modules::Backlight::Backlight(const std::string &id, const Json::Value &config) : ALabel(config, "backlight", id, "{percent}%", 2), preferred_device_(config["device"].isString() ? config["device"].asString() : "") { // Get initial state { std::unique_ptr udev_check{udev_new()}; check_nn(udev_check.get(), "Udev check new failed"); enumerate_devices( devices_.begin(), devices_.end(), std::back_inserter(devices_), udev_check.get()); if (devices_.empty()) { throw std::runtime_error("No backlight found"); } dp.emit(); } udev_thread_ = [this] { std::unique_ptr udev{udev_new()}; check_nn(udev.get(), "Udev new failed"); std::unique_ptr mon{ udev_monitor_new_from_netlink(udev.get(), "udev")}; check_nn(mon.get(), "udev monitor new failed"); check_gte(udev_monitor_filter_add_match_subsystem_devtype(mon.get(), "backlight", nullptr), 0, "udev failed to add monitor filter: "); udev_monitor_enable_receiving(mon.get()); auto udev_fd = udev_monitor_get_fd(mon.get()); auto epoll_fd = FileDescriptor{epoll_create1(EPOLL_CLOEXEC)}; check_neq(epoll_fd.get(), -1, "epoll init failed: "); epoll_event ctl_event{}; ctl_event.events = EPOLLIN; ctl_event.data.fd = udev_fd; check0(epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, ctl_event.data.fd, &ctl_event), "epoll_ctl failed: {}"); epoll_event events[EPOLL_MAX_EVENTS]; while (udev_thread_.isRunning()) { const int event_count = epoll_wait( epoll_fd.get(), events, EPOLL_MAX_EVENTS, std::chrono::milliseconds{interval_}.count()); if (!udev_thread_.isRunning()) { break; } decltype(devices_) devices; { std::scoped_lock lock(udev_thread_mutex_); devices = devices_; } for (int i = 0; i < event_count; ++i) { const auto &event = events[i]; check_eq(event.data.fd, udev_fd, "unexpected udev fd"); std::unique_ptr dev{udev_monitor_receive_device(mon.get())}; check_nn(dev.get(), "epoll dev was null"); upsert_device(devices.begin(), devices.end(), std::back_inserter(devices), dev.get()); } // Refresh state if timed out if (event_count == 0) { enumerate_devices(devices.begin(), devices.end(), std::back_inserter(devices), udev.get()); } { std::scoped_lock lock(udev_thread_mutex_); devices_ = devices; } dp.emit(); } }; } waybar::modules::Backlight::~Backlight() = default; auto waybar::modules::Backlight::update() -> void { decltype(devices_) devices; { std::scoped_lock lock(udev_thread_mutex_); devices = devices_; } const auto best = best_device(devices.cbegin(), devices.cend(), preferred_device_); if (best != nullptr) { if (previous_best_.has_value() && previous_best_.value() == *best && !previous_format_.empty() && previous_format_ == format_) { return; } const auto percent = best->get_max() == 0 ? 100 : best->get_actual() * 100 / best->get_max(); label_.set_markup(fmt::format( format_, fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent)))); getState(percent); } else { if (!previous_best_.has_value()) { return; } label_.set_markup(""); } previous_best_ = best == nullptr ? std::nullopt : std::optional{*best}; previous_format_ = format_; } template const waybar::modules::Backlight::BacklightDev *waybar::modules::Backlight::best_device( ForwardIt first, ForwardIt last, std::string_view preferred_device) { const auto found = std::find_if( first, last, [preferred_device](const auto &dev) { return dev.name() == preferred_device; }); if (found != last) { return &(*found); } const auto max = std::max_element( first, last, [](const auto &l, const auto &r) { return l.get_max() < r.get_max(); }); return max == last ? nullptr : &(*max); } template void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last, Inserter inserter, udev_device *dev) { const char *name = udev_device_get_sysname(dev); check_nn(name); const char *actual_brightness_attr = strcmp(name, "amdgpu_bl0") == 0 ? "brightness" : "actual_brightness"; const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr); check_nn(actual); const int actual_int = std::stoi(actual); const char *max = udev_device_get_sysattr_value(dev, "max_brightness"); check_nn(max); const int max_int = std::stoi(max); auto found = std::find_if(first, last, [name](const auto &device) { return device.name() == name; }); if (found != last) { found->set_actual(actual_int); found->set_max(max_int); } else { *inserter = BacklightDev{name, actual_int, max_int}; ++inserter; } } template void waybar::modules::Backlight::enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, udev *udev) { std::unique_ptr enumerate{udev_enumerate_new(udev)}; udev_enumerate_add_match_subsystem(enumerate.get(), "backlight"); udev_enumerate_scan_devices(enumerate.get()); udev_list_entry *enum_devices = udev_enumerate_get_list_entry(enumerate.get()); udev_list_entry *dev_list_entry; udev_list_entry_foreach(dev_list_entry, enum_devices) { const char * path = udev_list_entry_get_name(dev_list_entry); std::unique_ptr dev{udev_device_new_from_syspath(udev, path)}; check_nn(dev.get(), "dev new failed"); upsert_device(first, last, inserter, dev.get()); } } waybar-0.9.0/src/modules/battery.cpp000066400000000000000000000145521360163675500174450ustar00rootroot00000000000000#include "modules/battery.hpp" #include waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config) : ALabel(config, "battery", id, "{capacity}%", 60) { getBatteries(); fd_ = inotify_init1(IN_CLOEXEC); if (fd_ == -1) { throw std::runtime_error("Unable to listen batteries."); } for (auto const& bat : batteries_) { auto wd = inotify_add_watch(fd_, (bat / "uevent").c_str(), IN_ACCESS); if (wd != -1) { wds_.push_back(wd); } } worker(); } waybar::modules::Battery::~Battery() { for (auto wd : wds_) { inotify_rm_watch(fd_, wd); } close(fd_); } void waybar::modules::Battery::worker() { thread_timer_ = [this] { dp.emit(); thread_timer_.sleep_for(interval_); }; thread_ = [this] { struct inotify_event event = {0}; int nbytes = read(fd_, &event, sizeof(event)); if (nbytes != sizeof(event) || event.mask & IN_IGNORED) { thread_.stop(); return; } // TODO: don't stop timer for now since there is some bugs :? // thread_timer_.stop(); dp.emit(); }; } void waybar::modules::Battery::getBatteries() { try { for (auto& node : fs::directory_iterator(data_dir_)) { if (!fs::is_directory(node)) { continue; } auto dir_name = node.path().filename(); auto bat_defined = config_["bat"].isString(); if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) && fs::exists(node.path() / "capacity") && fs::exists(node.path() / "uevent") && fs::exists(node.path() / "status")) { batteries_.push_back(node.path()); } auto adap_defined = config_["adapter"].isString(); if (((adap_defined && dir_name == config_["adapter"].asString()) || !adap_defined) && fs::exists(node.path() / "online")) { adapter_ = node.path(); } } } catch (fs::filesystem_error& e) { throw std::runtime_error(e.what()); } if (batteries_.empty()) { if (config_["bat"].isString()) { throw std::runtime_error("No battery named " + config_["bat"].asString()); } throw std::runtime_error("No batteries."); } } const std::tuple waybar::modules::Battery::getInfos() const { try { uint16_t total = 0; uint32_t total_power = 0; // μW uint32_t total_energy = 0; // μWh uint32_t total_energy_full = 0; std::string status = "Unknown"; for (auto const& bat : batteries_) { uint16_t capacity; uint32_t power_now; uint32_t energy_full; uint32_t energy_now; std::string _status; std::ifstream(bat / "capacity") >> capacity; std::ifstream(bat / "status") >> _status; auto rate_path = fs::exists(bat / "current_now") ? "current_now" : "power_now"; std::ifstream(bat / rate_path) >> power_now; auto now_path = fs::exists(bat / "charge_now") ? "charge_now" : "energy_now"; std::ifstream(bat / now_path) >> energy_now; auto full_path = fs::exists(bat / "charge_full") ? "charge_full" : "energy_full"; std::ifstream(bat / full_path) >> energy_full; if (_status != "Unknown") { status = _status; } total += capacity; total_power += power_now; total_energy += energy_now; total_energy_full += energy_full; } if (!adapter_.empty() && status == "Discharging") { bool online; std::ifstream(adapter_ / "online") >> online; if (online) { status = "Plugged"; } } float time_remaining = 0; if (status == "Discharging" && total_power != 0) { time_remaining = (float)total_energy / total_power; } else if (status == "Charging" && total_power != 0) { time_remaining = -(float)(total_energy_full - total_energy) / total_power; } uint16_t capacity = total / batteries_.size(); return {capacity, time_remaining, status}; } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); return {0, 0, "Unknown"}; } } const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) const { if (!adapter_.empty()) { bool online; std::ifstream(adapter_ / "online") >> online; if (capacity == 100) { return "Full"; } if (online) { return "Plugged"; } return "Discharging"; } return "Unknown"; } const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemaining) { hoursRemaining = std::fabs(hoursRemaining); uint16_t full_hours = static_cast(hoursRemaining); uint16_t minutes = static_cast(60 * (hoursRemaining - full_hours)); auto format = std::string("{H} h {M} min"); if (config_["format-time"].isString()) { format = config_["format-time"].asString(); } return fmt::format(format, fmt::arg("H", full_hours), fmt::arg("M", minutes)); } auto waybar::modules::Battery::update() -> void { auto [capacity, time_remaining, status] = getInfos(); if (status == "Unknown") { status = getAdapterStatus(capacity); } if (tooltipEnabled()) { std::string tooltip_text; if (time_remaining != 0) { std::string time_to = std::string("Time to ") + ((time_remaining > 0) ? "empty" : "full"); tooltip_text = time_to + ": " + formatTimeRemaining(time_remaining); } else { tooltip_text = status; } label_.set_tooltip_text(tooltip_text); } std::transform(status.begin(), status.end(), status.begin(), ::tolower); auto format = format_; auto state = getState(capacity, true); if (!old_status_.empty()) { label_.get_style_context()->remove_class(old_status_); } label_.get_style_context()->add_class(status); old_status_ = status; if (!state.empty() && config_["format-" + status + "-" + state].isString()) { format = config_["format-" + status + "-" + state].asString(); } else if (config_["format-" + status].isString()) { format = config_["format-" + status].asString(); } else if (!state.empty() && config_["format-" + state].isString()) { format = config_["format-" + state].asString(); } if (format.empty()) { event_box_.hide(); } else { event_box_.show(); label_.set_markup(fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("icon", getIcon(capacity, state)), fmt::arg("time", formatTimeRemaining(time_remaining)))); } } waybar-0.9.0/src/modules/clock.cpp000066400000000000000000000021011360163675500170510ustar00rootroot00000000000000#include "modules/clock.hpp" #include waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60) { thread_ = [this] { dp.emit(); auto now = std::chrono::system_clock::now(); auto timeout = std::chrono::floor(now + interval_); auto diff = std::chrono::seconds(timeout.time_since_epoch().count() % interval_.count()); thread_.sleep_until(timeout - diff); }; } auto waybar::modules::Clock::update() -> void { tzset(); // Update timezone information auto now = std::chrono::system_clock::now(); auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); auto text = fmt::format(format_, localtime); label_.set_markup(text); if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { auto tooltip_format = config_["tooltip-format"].asString(); auto tooltip_text = fmt::format(tooltip_format, localtime); label_.set_tooltip_text(tooltip_text); } else { label_.set_tooltip_text(text); } } } waybar-0.9.0/src/modules/cpu.cpp000066400000000000000000000051641360163675500165610ustar00rootroot00000000000000#include "modules/cpu.hpp" #include waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config) : ALabel(config, "cpu", id, "{usage}%", 10) { thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); }; } auto waybar::modules::Cpu::update() -> void { // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both auto cpu_load = getCpuLoad(); auto [cpu_usage, tooltip] = getCpuUsage(); if (tooltipEnabled()) { label_.set_tooltip_text(tooltip); } label_.set_markup(fmt::format(format_, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage))); getState(cpu_usage); } uint16_t waybar::modules::Cpu::getCpuLoad() { struct sysinfo info = {0}; if (sysinfo(&info) == 0) { float f_load = 1.F / (1U << SI_LOAD_SHIFT); uint16_t load = info.loads[0] * f_load * 100 / get_nprocs(); return load; } throw std::runtime_error("Can't get Cpu load"); } std::tuple waybar::modules::Cpu::getCpuUsage() { if (prev_times_.empty()) { prev_times_ = parseCpuinfo(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::vector> curr_times = parseCpuinfo(); std::string tooltip; uint16_t usage = 0; for (size_t i = 0; i < curr_times.size(); ++i) { auto [curr_idle, curr_total] = curr_times[i]; auto [prev_idle, prev_total] = prev_times_[i]; const float delta_idle = curr_idle - prev_idle; const float delta_total = curr_total - prev_total; uint16_t tmp = 100 * (1 - delta_idle / delta_total); if (i == 0) { usage = tmp; tooltip = fmt::format("Total: {}%", tmp); } else { tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp); } } prev_times_ = curr_times; return {usage, tooltip}; } std::vector> waybar::modules::Cpu::parseCpuinfo() { std::ifstream info(data_dir_); if (!info.is_open()) { throw std::runtime_error("Can't open " + data_dir_); } std::vector> cpuinfo; std::string line; while (getline(info, line)) { if (line.substr(0, 3).compare("cpu") != 0) { break; } std::stringstream sline(line.substr(5)); std::vector times; for (size_t time = 0; sline >> time; times.push_back(time)) ; size_t idle_time = 0; size_t total_time = 0; if (times.size() >= 4) { idle_time = times[3]; total_time = std::accumulate(times.begin(), times.end(), 0); } cpuinfo.emplace_back(idle_time, total_time); } return cpuinfo; } waybar-0.9.0/src/modules/custom.cpp000066400000000000000000000117471360163675500173100ustar00rootroot00000000000000#include "modules/custom.hpp" #include waybar::modules::Custom::Custom(const std::string& name, const std::string& id, const Json::Value& config) : ALabel(config, "custom-" + name, id, "{}"), name_(name), fp_(nullptr), pid_(-1) { if (config_["exec"].isString()) { if (interval_.count() > 0) { delayWorker(); } else { continuousWorker(); } } dp.emit(); } waybar::modules::Custom::~Custom() { if (pid_ != -1) { kill(-pid_, 9); pid_ = -1; } } void waybar::modules::Custom::delayWorker() { thread_ = [this] { bool can_update = true; if (config_["exec-if"].isString()) { auto res = util::command::exec(config_["exec-if"].asString()); if (res.exit_code != 0) { can_update = false; event_box_.hide(); } } if (can_update) { output_ = util::command::exec(config_["exec"].asString()); dp.emit(); } thread_.sleep_for(interval_); }; } void waybar::modules::Custom::continuousWorker() { auto cmd = config_["exec"].asString(); pid_ = -1; fp_ = util::command::open(cmd, pid_); if (!fp_) { throw std::runtime_error("Unable to open " + cmd); } thread_ = [&] { char* buff = nullptr; size_t len = 0; if (getline(&buff, &len, fp_) == -1) { int exit_code = 1; if (fp_) { exit_code = WEXITSTATUS(util::command::close(fp_, pid_)); fp_ = nullptr; } thread_.stop(); if (exit_code != 0) { output_ = {exit_code, ""}; dp.emit(); spdlog::error("{} stopped unexpectedly, is it endless?", name_); } return; } std::string output = buff; // Remove last newline if (!output.empty() && output[output.length() - 1] == '\n') { output.erase(output.length() - 1); } output_ = {0, output}; dp.emit(); }; } void waybar::modules::Custom::refresh(int sig) { if (sig == SIGRTMIN + config_["signal"].asInt()) { thread_.wake_up(); } } bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) { auto ret = ALabel::handleScroll(e); thread_.wake_up(); return ret; } bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) { auto ret = ALabel::handleToggle(e); thread_.wake_up(); return ret; } auto waybar::modules::Custom::update() -> void { // Hide label if output is empty if (config_["exec"].isString() && (output_.out.empty() || output_.exit_code != 0)) { event_box_.hide(); } else { if (config_["return-type"].asString() == "json") { parseOutputJson(); } else { parseOutputRaw(); } auto str = fmt::format(format_, text_, fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); if (str.empty()) { event_box_.hide(); } else { label_.set_markup(str); if (tooltipEnabled()) { if (text_ == tooltip_) { label_.set_tooltip_text(str); } else { label_.set_tooltip_text(tooltip_); } } auto classes = label_.get_style_context()->list_classes(); for (auto const& c : classes) { label_.get_style_context()->remove_class(c); } for (auto const& c : class_) { label_.get_style_context()->add_class(c); } event_box_.show(); } } } void waybar::modules::Custom::parseOutputRaw() { std::istringstream output(output_.out); std::string line; int i = 0; while (getline(output, line)) { if (i == 0) { if (config_["escape"].isBool() && config_["escape"].asBool()) { text_ = Glib::Markup::escape_text(line); } else { text_ = line; } tooltip_ = line; class_.clear(); } else if (i == 1) { tooltip_ = line; } else if (i == 2) { class_.push_back(line); } else { break; } i++; } } void waybar::modules::Custom::parseOutputJson() { std::istringstream output(output_.out); std::string line; class_.clear(); while (getline(output, line)) { auto parsed = parser_.parse(line); if (config_["escape"].isBool() && config_["escape"].asBool()) { text_ = Glib::Markup::escape_text(parsed["text"].asString()); } else { text_ = parsed["text"].asString(); } if (config_["escape"].isBool() && config_["escape"].asBool()) { alt_ = Glib::Markup::escape_text(parsed["alt"].asString()); } else { alt_ = parsed["alt"].asString(); } tooltip_ = parsed["tooltip"].asString(); if (parsed["class"].isString()) { class_.push_back(parsed["class"].asString()); } else if (parsed["class"].isArray()) { for (auto const& c : parsed["class"]) { class_.push_back(c.asString()); } } if (!parsed["percentage"].asString().empty() && parsed["percentage"].isUInt()) { percentage_ = parsed["percentage"].asUInt(); } else { percentage_ = 0; } break; } } waybar-0.9.0/src/modules/disk.cpp000066400000000000000000000051751360163675500167260ustar00rootroot00000000000000#include "modules/disk.hpp" using namespace waybar::util; waybar::modules::Disk::Disk(const std::string& id, const Json::Value& config) : ALabel(config, "disk", id, "{}%", 30) , path_("/") { thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); }; if (config["path"].isString()) { path_ = config["path"].asString(); } } auto waybar::modules::Disk::update() -> void { struct statvfs /* { unsigned long f_bsize; // filesystem block size unsigned long f_frsize; // fragment size fsblkcnt_t f_blocks; // size of fs in f_frsize units fsblkcnt_t f_bfree; // # free blocks fsblkcnt_t f_bavail; // # free blocks for unprivileged users fsfilcnt_t f_files; // # inodes fsfilcnt_t f_ffree; // # free inodes fsfilcnt_t f_favail; // # free inodes for unprivileged users unsigned long f_fsid; // filesystem ID unsigned long f_flag; // mount flags unsigned long f_namemax; // maximum filename length }; */ stats; int err = statvfs(path_.c_str(), &stats); /* Conky options fs_bar - Bar that shows how much space is used fs_free - Free space on a file system fs_free_perc - Free percentage of space fs_size - File system size fs_used - File system used space */ if (err != 0) { event_box_.hide(); return; } auto free = pow_format(stats.f_bavail * stats.f_bsize, "B", true); auto used = pow_format((stats.f_blocks - stats.f_bavail) * stats.f_bsize, "B", true); auto total = pow_format(stats.f_blocks * stats.f_bsize, "B", true); label_.set_markup(fmt::format(format_ , stats.f_bavail * 100 / stats.f_blocks , fmt::arg("free", free) , fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks) , fmt::arg("used", used) , fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks) , fmt::arg("total", total) , fmt::arg("path", path_) )); if (tooltipEnabled()) { std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)"; if (config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } label_.set_tooltip_text(fmt::format(tooltip_format , stats.f_bavail * 100 / stats.f_blocks , fmt::arg("free", free) , fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks) , fmt::arg("used", used) , fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks) , fmt::arg("total", total) , fmt::arg("path", path_) )); } event_box_.show(); } waybar-0.9.0/src/modules/idle_inhibitor.cpp000066400000000000000000000031731360163675500207540ustar00rootroot00000000000000#include "modules/idle_inhibitor.hpp" #include "util/command.hpp" waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "idle_inhibitor", id, "{status}"), bar_(bar), status_("deactivated"), idle_inhibitor_(nullptr), pid_(-1) { event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect( sigc::mem_fun(*this, &IdleInhibitor::handleToggle)); dp.emit(); } waybar::modules::IdleInhibitor::~IdleInhibitor() { if (idle_inhibitor_ != nullptr) { zwp_idle_inhibitor_v1_destroy(idle_inhibitor_); idle_inhibitor_ = nullptr; } if (pid_ != -1) { kill(-pid_, 9); pid_ = -1; } } auto waybar::modules::IdleInhibitor::update() -> void { label_.set_markup( fmt::format(format_, fmt::arg("status", status_), fmt::arg("icon", getIcon(0, status_)))); label_.get_style_context()->add_class(status_); if (tooltipEnabled()) { label_.set_tooltip_text(status_); } } bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) { if (e->button == 1) { label_.get_style_context()->remove_class(status_); if (idle_inhibitor_ != nullptr) { zwp_idle_inhibitor_v1_destroy(idle_inhibitor_); idle_inhibitor_ = nullptr; status_ = "deactivated"; } else { idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor( waybar::Client::inst()->idle_inhibit_manager, bar_.surface); status_ = "activated"; } click_param = status_; } ALabel::handleToggle(e); return true; } waybar-0.9.0/src/modules/memory.cpp000066400000000000000000000043751360163675500173050ustar00rootroot00000000000000#include "modules/memory.hpp" waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config) : ALabel(config, "memory", id, "{}%", 30) { thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); }; } auto waybar::modules::Memory::update() -> void { parseMeminfo(); if (memtotal_ > 0 && memfree_ >= 0) { auto total_ram_gigabytes = memtotal_ / std::pow(1024, 2); int used_ram_percentage = 100 * (memtotal_ - memfree_) / memtotal_; auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2); auto available_ram_gigabytes = memfree_ / std::pow(1024, 2); getState(used_ram_percentage); label_.set_markup(fmt::format(format_, used_ram_percentage, fmt::arg("total", total_ram_gigabytes), fmt::arg("percentage", used_ram_percentage), fmt::arg("used", used_ram_gigabytes), fmt::arg("avail", available_ram_gigabytes))); if (tooltipEnabled()) { label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1)); } event_box_.show(); } else { event_box_.hide(); } } void waybar::modules::Memory::parseMeminfo() { int64_t memfree = -1, membuffer = -1, memcache = -1, memavail = -1; std::ifstream info(data_dir_); if (!info.is_open()) { throw std::runtime_error("Can't open " + data_dir_); } std::string line; while (getline(info, line)) { auto posDelim = line.find(':'); if (posDelim == std::string::npos) { continue; } std::string name = line.substr(0, posDelim); int64_t value = std::stol(line.substr(posDelim + 1)); if (name.compare("MemTotal") == 0) { memtotal_ = value; } else if (name.compare("MemAvailable") == 0) { memavail = value; } else if (name.compare("MemFree") == 0) { memfree = value; } else if (name.compare("Buffers") == 0) { membuffer = value; } else if (name.compare("Cached") == 0) { memcache = value; } if (memtotal_ > 0 && (memavail >= 0 || (memfree > -1 && membuffer > -1 && memcache > -1))) { break; } } memfree_ = memavail >= 0 ? memavail : memfree + membuffer + memcache; } waybar-0.9.0/src/modules/mpd.cpp000066400000000000000000000270371360163675500165550ustar00rootroot00000000000000#include "modules/mpd.hpp" #include #include waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) : ALabel(config, "mpd", id, "{album} - {artist} - {title}", 5), module_name_(id.empty() ? "mpd" : "mpd#" + id), server_(nullptr), port_(config_["port"].isUInt() ? config["port"].asUInt() : 0), timeout_(config_["timeout"].isUInt() ? config_["timeout"].asUInt() * 1'000 : 30'000), connection_(nullptr, &mpd_connection_free), alternate_connection_(nullptr, &mpd_connection_free), status_(nullptr, &mpd_status_free), song_(nullptr, &mpd_song_free) { if (!config_["port"].isNull() && !config_["port"].isUInt()) { spdlog::warn("{}: `port` configuration should be an unsigned int", module_name_); } if (!config_["timeout"].isNull() && !config_["timeout"].isUInt()) { spdlog::warn("{}: `timeout` configuration should be an unsigned int", module_name_); } if (!config["server"].isNull()) { if (!config_["server"].isString()) { spdlog::warn("{}:`server` configuration should be a string", module_name_); } server_ = config["server"].asCString(); } event_listener().detach(); event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &MPD::handlePlayPause)); } auto waybar::modules::MPD::update() -> void { std::lock_guard guard(connection_lock_); tryConnect(); if (connection_ != nullptr) { try { bool wasPlaying = playing(); if(!wasPlaying) { // Wait until the periodic_updater has stopped std::lock_guard periodic_guard(periodic_lock_); } fetchState(); if (!wasPlaying && playing()) { periodic_updater().detach(); } } catch (const std::exception& e) { spdlog::error("{}: {}", module_name_, e.what()); state_ = MPD_STATE_UNKNOWN; } } setLabel(); } std::thread waybar::modules::MPD::event_listener() { return std::thread([this] { while (true) { try { if (connection_ == nullptr) { // Retry periodically if no connection dp.emit(); std::this_thread::sleep_for(interval_); } else { waitForEvent(); dp.emit(); } } catch (const std::exception& e) { spdlog::warn("{}: {}", module_name_, e.what()); } } }); } std::thread waybar::modules::MPD::periodic_updater() { return std::thread([this] { std::lock_guard guard(periodic_lock_); while (connection_ != nullptr && playing()) { dp.emit(); std::this_thread::sleep_for(std::chrono::seconds(1)); } }); } std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) { std::string result = config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A"; const char* tag = mpd_song_get_tag(song_.get(), type, idx); // mpd_song_get_tag can return NULL, so make sure it's valid before setting if (tag) result = tag; return result; } void waybar::modules::MPD::setLabel() { if (connection_ == nullptr) { label_.get_style_context()->add_class("disconnected"); label_.get_style_context()->remove_class("stopped"); label_.get_style_context()->remove_class("playing"); label_.get_style_context()->remove_class("paused"); auto format = config_["format-disconnected"].isString() ? config_["format-disconnected"].asString() : "disconnected"; label_.set_markup(format); if (tooltipEnabled()) { std::string tooltip_format; tooltip_format = config_["tooltip-format-disconnected"].isString() ? config_["tooltip-format-disconnected"].asString() : "MPD (disconnected)"; // Nothing to format label_.set_tooltip_text(tooltip_format); } return; } else { label_.get_style_context()->remove_class("disconnected"); } auto format = format_; std::string artist, album_artist, album, title, date; std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; if (stopped()) { format = config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped"; label_.get_style_context()->add_class("stopped"); label_.get_style_context()->remove_class("playing"); label_.get_style_context()->remove_class("paused"); } else { label_.get_style_context()->remove_class("stopped"); if (playing()) { label_.get_style_context()->add_class("playing"); label_.get_style_context()->remove_class("paused"); } else { label_.get_style_context()->add_class("paused"); label_.get_style_context()->remove_class("playing"); } stateIcon = getStateIcon(); artist = getTag(MPD_TAG_ARTIST); album_artist = getTag(MPD_TAG_ALBUM_ARTIST); album = getTag(MPD_TAG_ALBUM); title = getTag(MPD_TAG_TITLE); date = getTag(MPD_TAG_DATE); elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get())); totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get())); } bool consumeActivated = mpd_status_get_consume(status_.get()); std::string consumeIcon = getOptionIcon("consume", consumeActivated); bool randomActivated = mpd_status_get_random(status_.get()); std::string randomIcon = getOptionIcon("random", randomActivated); bool repeatActivated = mpd_status_get_repeat(status_.get()); std::string repeatIcon = getOptionIcon("repeat", repeatActivated); bool singleActivated = mpd_status_get_single(status_.get()); std::string singleIcon = getOptionIcon("single", singleActivated); // TODO: format can fail label_.set_markup( fmt::format(format, fmt::arg("artist", Glib::Markup::escape_text(artist).raw()), fmt::arg("albumArtist", Glib::Markup::escape_text(album_artist).raw()), fmt::arg("album", Glib::Markup::escape_text(album).raw()), fmt::arg("title", Glib::Markup::escape_text(title).raw()), fmt::arg("date", Glib::Markup::escape_text(date).raw()), fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon))); if (tooltipEnabled()) { std::string tooltip_format; tooltip_format = config_["tooltip-format"].isString() ? config_["tooltip-format"].asString() : "MPD (connected)"; auto tooltip_text = fmt::format(tooltip_format, fmt::arg("artist", artist), fmt::arg("albumArtist", album_artist), fmt::arg("album", album), fmt::arg("title", title), fmt::arg("date", date), fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon)); label_.set_tooltip_text(tooltip_text); } } std::string waybar::modules::MPD::getStateIcon() { if (!config_["state-icons"].isObject()) { return ""; } if (connection_ == nullptr) { spdlog::warn("{}: Trying to fetch state icon while disconnected", module_name_); return ""; } if (stopped()) { spdlog::warn("{}: Trying to fetch state icon while stopped", module_name_); return ""; } if (playing()) { return config_["state-icons"]["playing"].asString(); } else { return config_["state-icons"]["paused"].asString(); } } std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) { if (!config_[optionName + "-icons"].isObject()) { return ""; } if (connection_ == nullptr) { spdlog::warn("{}: Trying to fetch option icon while disconnected", module_name_); return ""; } if (activated) { return config_[optionName + "-icons"]["on"].asString(); } else { return config_[optionName + "-icons"]["off"].asString(); } } void waybar::modules::MPD::tryConnect() { if (connection_ != nullptr) { return; } connection_ = unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); alternate_connection_ = unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); if (connection_ == nullptr || alternate_connection_ == nullptr) { spdlog::error("{}: Failed to connect to MPD", module_name_); connection_.reset(); alternate_connection_.reset(); return; } try { checkErrors(connection_.get()); spdlog::info("{}: Connected to MPD", module_name_); } catch (std::runtime_error& e) { spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what()); connection_.reset(); alternate_connection_.reset(); } } void waybar::modules::MPD::checkErrors(mpd_connection* conn) { switch (mpd_connection_get_error(conn)) { case MPD_ERROR_SUCCESS: mpd_connection_clear_error(conn); return; case MPD_ERROR_TIMEOUT: case MPD_ERROR_CLOSED: mpd_connection_clear_error(conn); connection_.reset(); alternate_connection_.reset(); state_ = MPD_STATE_UNKNOWN; throw std::runtime_error("Connection to MPD closed"); default: auto error_message = mpd_connection_get_error_message(conn); mpd_connection_clear_error(conn); throw std::runtime_error(std::string(error_message)); } } void waybar::modules::MPD::fetchState() { auto conn = connection_.get(); status_ = unique_status(mpd_run_status(conn), &mpd_status_free); checkErrors(conn); state_ = mpd_status_get_state(status_.get()); checkErrors(conn); song_ = unique_song(mpd_run_current_song(conn), &mpd_song_free); checkErrors(conn); } void waybar::modules::MPD::waitForEvent() { auto conn = alternate_connection_.get(); // Wait for a player (play/pause), option (random, shuffle, etc.), or playlist // change if (!mpd_send_idle_mask( conn, static_cast(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) { checkErrors(conn); return; } // alternate_idle_ = true; // See issue #277: // https://github.com/Alexays/Waybar/issues/277 mpd_recv_idle(conn, /* disable_timeout = */ false); // See issue #281: // https://github.com/Alexays/Waybar/issues/281 std::lock_guard guard(connection_lock_); checkErrors(conn); mpd_response_finish(conn); checkErrors(conn); } bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) { if (e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS || connection_ == nullptr) { return false; } if (e->button == 1) { std::lock_guard guard(connection_lock_); if (stopped()) { mpd_run_play(connection_.get()); } else { mpd_run_toggle_pause(connection_.get()); } } else if (e->button == 3) { std::lock_guard guard(connection_lock_); mpd_run_stop(connection_.get()); } return true; } bool waybar::modules::MPD::stopped() { return connection_ == nullptr || state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP; } bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; } waybar-0.9.0/src/modules/network.cpp000066400000000000000000000613211360163675500174600ustar00rootroot00000000000000#include "modules/network.hpp" #include #include #include #include "util/format.hpp" namespace { using namespace waybar::util; constexpr const char *NETSTAT_FILE = "/proc/net/netstat"; // std::ifstream does not take std::string_view as param constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt"; constexpr std::string_view BANDWIDTH_DOWN_TOTAL_KEY = "InOctets"; constexpr std::string_view BANDWIDTH_UP_TOTAL_KEY = "OutOctets"; std::ifstream netstat(NETSTAT_FILE); std::optional read_netstat(std::string_view category, std::string_view key) { if (!netstat) { spdlog::warn("Failed to open netstat file {}", NETSTAT_FILE); return {}; } netstat.seekg(std::ios_base::beg); // finding corresponding line (category) // looks into the file for the first line starting by the 'category' string auto starts_with = [](const std::string &str, std::string_view start) { return start == std::string_view{str.data(), std::min(str.size(), start.size())}; }; std::string read; while (std::getline(netstat, read) && !starts_with(read, category)) ; if (!starts_with(read, category)) { spdlog::warn("Category '{}' not found in netstat file {}", category, NETSTAT_FILE); return {}; } // finding corresponding column (key) // looks into the fetched line for the first word (space separated) equal to 'key' int index = 0; auto r_it = read.begin(); auto k_it = key.begin(); while (k_it != key.end() && r_it != read.end()) { if (*r_it != *k_it) { r_it = std::find(r_it, read.end(), ' '); if (r_it != read.end()) { ++r_it; } k_it = key.begin(); ++index; } else { ++r_it; ++k_it; } } if (r_it == read.end() && k_it != key.end()) { spdlog::warn( "Key '{}' not found in category '{}' of netstat file {}", key, category, NETSTAT_FILE); return {}; } // finally accessing value // accesses the line right under the fetched one std::getline(netstat, read); assert(starts_with(read, category)); std::istringstream iss(read); while (index--) { std::getline(iss, read, ' '); } unsigned long long value; iss >> value; return value; } } // namespace waybar::modules::Network::Network(const std::string &id, const Json::Value &config) : ALabel(config, "network", id, "{ifname}", 60), ifid_(-1), family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), ev_fd_(-1), cidr_(-1), signal_strength_dbm_(0), signal_strength_(0), frequency_(0) { auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY); auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY); if (down_octets) { bandwidth_down_total_ = *down_octets; } else { bandwidth_down_total_ = 0; } if (up_octets) { bandwidth_up_total_ = *up_octets; } else { bandwidth_up_total_ = 0; } createEventSocket(); createInfoSocket(); auto default_iface = getPreferredIface(-1, false); if (default_iface != -1) { ifid_ = default_iface; char ifname[IF_NAMESIZE]; if_indextoname(default_iface, ifname); ifname_ = ifname; getInterfaceAddress(); } dp.emit(); worker(); } waybar::modules::Network::~Network() { if (ev_fd_ > -1) { eventfd_write(ev_fd_, 1); std::this_thread::sleep_for(std::chrono::milliseconds(150)); close(ev_fd_); } if (efd_ > -1) { close(efd_); } if (ev_sock_ != nullptr) { nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK); if (family_ == AF_INET) { nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV4_IFADDR); } else { nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); } nl_close(ev_sock_); nl_socket_free(ev_sock_); } if (sock_ != nullptr) { nl_close(sock_); nl_socket_free(sock_); } } void waybar::modules::Network::createEventSocket() { ev_sock_ = nl_socket_alloc(); nl_socket_disable_seq_check(ev_sock_); nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this); auto groups = RTMGRP_LINK | (family_ == AF_INET ? RTMGRP_IPV4_IFADDR : RTMGRP_IPV6_IFADDR); nl_join_groups(ev_sock_, groups); // Deprecated if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) { throw std::runtime_error("Can't connect network socket"); } nl_socket_add_membership(ev_sock_, RTNLGRP_LINK); if (family_ == AF_INET) { nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR); } else { nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); } efd_ = epoll_create1(EPOLL_CLOEXEC); if (efd_ < 0) { throw std::runtime_error("Can't create epoll"); } { ev_fd_ = eventfd(0, EFD_NONBLOCK); struct epoll_event event; memset(&event, 0, sizeof(event)); event.events = EPOLLIN | EPOLLET; event.data.fd = ev_fd_; if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) { throw std::runtime_error("Can't add epoll event"); } } { auto fd = nl_socket_get_fd(ev_sock_); struct epoll_event event; memset(&event, 0, sizeof(event)); event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; event.data.fd = fd; if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) { throw std::runtime_error("Can't add epoll event"); } } } void waybar::modules::Network::createInfoSocket() { sock_ = nl_socket_alloc(); if (genl_connect(sock_) != 0) { throw std::runtime_error("Can't connect to netlink socket"); } if (nl_socket_modify_cb(sock_, NL_CB_VALID, NL_CB_CUSTOM, handleScan, this) < 0) { throw std::runtime_error("Can't set callback"); } nl80211_id_ = genl_ctrl_resolve(sock_, "nl80211"); if (nl80211_id_ < 0) { throw std::runtime_error("Can't resolve nl80211 interface"); } } void waybar::modules::Network::worker() { thread_timer_ = [this] { { std::lock_guard lock(mutex_); if (ifid_ > 0) { getInfo(); dp.emit(); } } thread_timer_.sleep_for(interval_); }; thread_ = [this] { std::array events{}; int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1); if (ec > 0) { for (auto i = 0; i < ec; i++) { if (events[i].data.fd != nl_socket_get_fd(ev_sock_) || nl_recvmsgs_default(ev_sock_) < 0) { thread_.stop(); break; } } } }; } const std::string waybar::modules::Network::getNetworkState() const { if (ifid_ == -1) return "disconnected"; if (ipaddr_.empty()) return "linked"; if (essid_.empty()) return "ethernet"; return "wifi"; } auto waybar::modules::Network::update() -> void { std::lock_guard lock(mutex_); std::string tooltip_format; auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY); auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY); unsigned long long bandwidth_down = 0; if (down_octets) { bandwidth_down = *down_octets - bandwidth_down_total_; bandwidth_down_total_ = *down_octets; } unsigned long long bandwidth_up = 0; if (up_octets) { bandwidth_up = *up_octets - bandwidth_up_total_; bandwidth_up_total_ = *up_octets; } if (!alt_) { auto state = getNetworkState(); if (!state_.empty() && label_.get_style_context()->has_class(state_)) { label_.get_style_context()->remove_class(state_); } if (config_["format-" + state].isString()) { default_format_ = config_["format-" + state].asString(); } if (config_["tooltip-format-" + state].isString()) { tooltip_format = config_["tooltip-format-" + state].asString(); } if (!label_.get_style_context()->has_class(state)) { label_.get_style_context()->add_class(state); } format_ = default_format_; state_ = state; } getState(signal_strength_); auto text = fmt::format( format_, fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("cidr", cidr_), fmt::arg("frequency", frequency_), fmt::arg("icon", getIcon(signal_strength_, state_)), fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")), fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")), fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")), fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s"))); if (text != label_.get_label()) { label_.set_markup(text); } if (tooltipEnabled()) { if (tooltip_format.empty() && config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } if (!tooltip_format.empty()) { auto tooltip_text = fmt::format( tooltip_format, fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("cidr", cidr_), fmt::arg("frequency", frequency_), fmt::arg("icon", getIcon(signal_strength_, state_)), fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")), fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")), fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")), fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s"))); if (label_.get_tooltip_text() != text) { label_.set_tooltip_text(tooltip_text); } } else if (label_.get_tooltip_text() != text) { label_.set_tooltip_text(text); } } } // Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698 int waybar::modules::Network::getExternalInterface(int skip_idx) const { static const uint32_t route_buffer_size = 8192; struct nlmsghdr * hdr = nullptr; struct rtmsg * rt = nullptr; char resp[route_buffer_size] = {0}; int ifidx = -1; /* Prepare request. */ constexpr uint32_t reqlen = NLMSG_SPACE(sizeof(*rt)); char req[reqlen] = {0}; /* Build the RTM_GETROUTE request. */ hdr = reinterpret_cast(req); hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*rt)); hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; hdr->nlmsg_type = RTM_GETROUTE; rt = static_cast(NLMSG_DATA(hdr)); rt->rtm_family = family_; rt->rtm_table = RT_TABLE_MAIN; /* Issue the query. */ if (netlinkRequest(req, reqlen) < 0) { goto out; } /* Read the response(s). * * WARNING: All the packets generated by the request must be consumed (as in, * consume responses till NLMSG_DONE/NLMSG_ERROR is encountered). */ do { auto len = netlinkResponse(resp, route_buffer_size); if (len < 0) { goto out; } /* Parse the response payload into netlink messages. */ for (hdr = reinterpret_cast(resp); NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { if (hdr->nlmsg_type == NLMSG_DONE) { goto out; } if (hdr->nlmsg_type == NLMSG_ERROR) { /* Even if we found the interface index, something is broken with the * netlink socket, so return an error. */ ifidx = -1; goto out; } /* If we found the correct answer, skip parsing the attributes. */ if (ifidx != -1) { continue; } /* Find the message(s) concerting the main routing table, each message * corresponds to a single routing table entry. */ rt = static_cast(NLMSG_DATA(hdr)); if (rt->rtm_table != RT_TABLE_MAIN) { continue; } /* Parse all the attributes for a single routing table entry. */ struct rtattr *attr = RTM_RTA(rt); uint64_t attrlen = RTM_PAYLOAD(hdr); bool has_gateway = false; bool has_destination = false; int temp_idx = -1; for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) { /* Determine if this routing table entry corresponds to the default * route by seeing if it has a gateway, and if a destination addr is * set, that it is all 0s. */ switch (attr->rta_type) { case RTA_GATEWAY: /* The gateway of the route. * * If someone every needs to figure out the gateway address as well, * it's here as the attribute payload. */ has_gateway = true; break; case RTA_DST: { /* The destination address. * Should be either missing, or maybe all 0s. Accept both. */ const uint32_t nr_zeroes = (family_ == AF_INET) ? 4 : 16; unsigned char c = 0; size_t dstlen = RTA_PAYLOAD(attr); if (dstlen != nr_zeroes) { break; } for (uint32_t i = 0; i < dstlen; i += 1) { c |= *((unsigned char *)RTA_DATA(attr) + i); } has_destination = (c == 0); break; } case RTA_OIF: /* The output interface index. */ temp_idx = *static_cast(RTA_DATA(attr)); break; default: break; } } /* If this is the default route, and we know the interface index, * we can stop parsing this message. */ if (has_gateway && !has_destination && temp_idx != -1 && temp_idx != skip_idx) { ifidx = temp_idx; break; } } } while (true); out: return ifidx; } void waybar::modules::Network::getInterfaceAddress() { unsigned int cidrRaw; struct ifaddrs *ifaddr, *ifa; cidr_ = 0; int success = getifaddrs(&ifaddr); if (success != 0) { return; } ifa = ifaddr; while (ifa != nullptr) { if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ && ifa->ifa_name == ifname_) { char ipaddr[INET6_ADDRSTRLEN]; ipaddr_ = inet_ntop(family_, &reinterpret_cast(ifa->ifa_addr)->sin_addr, ipaddr, INET6_ADDRSTRLEN); char netmask[INET6_ADDRSTRLEN]; auto net_addr = reinterpret_cast(ifa->ifa_netmask); netmask_ = inet_ntop(family_, &net_addr->sin_addr, netmask, INET6_ADDRSTRLEN); cidrRaw = net_addr->sin_addr.s_addr; unsigned int cidr = 0; while (cidrRaw) { cidr += cidrRaw & 1; cidrRaw >>= 1; } cidr_ = cidr; break; } ifa = ifa->ifa_next; } freeifaddrs(ifaddr); } int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) const { struct sockaddr_nl sa = {}; sa.nl_family = AF_NETLINK; sa.nl_groups = groups; struct iovec iov = {req, reqlen}; struct msghdr msg = { .msg_name = &sa, .msg_namelen = sizeof(sa), .msg_iov = &iov, .msg_iovlen = 1, }; return sendmsg(nl_socket_get_fd(ev_sock_), &msg, 0); } int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint32_t groups) const { struct sockaddr_nl sa = {}; sa.nl_family = AF_NETLINK; sa.nl_groups = groups; struct iovec iov = {resp, resplen}; struct msghdr msg = { .msg_name = &sa, .msg_namelen = sizeof(sa), .msg_iov = &iov, .msg_iovlen = 1, }; auto ret = recvmsg(nl_socket_get_fd(ev_sock_), &msg, 0); if (msg.msg_flags & MSG_TRUNC) { return -1; } return ret; } bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::string name) { if (config_["interface"].isString()) { return config_["interface"].asString() == name || wildcardMatch(config_["interface"].asString(), name); } // getExternalInterface may need some delay to detect external interface for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) { auto external_iface = getExternalInterface(); if (external_iface > 0) { return external_iface == rtif->ifi_index; } std::this_thread::sleep_for(std::chrono::milliseconds(500)); } return false; } int waybar::modules::Network::getPreferredIface(int skip_idx, bool wait) const { int ifid = -1; if (config_["interface"].isString()) { ifid = if_nametoindex(config_["interface"].asCString()); if (ifid > 0) { return ifid; } else { // Try with wildcard struct ifaddrs *ifaddr, *ifa; int success = getifaddrs(&ifaddr); if (success != 0) { return -1; } ifa = ifaddr; ifid = -1; while (ifa != nullptr) { if (wildcardMatch(config_["interface"].asString(), ifa->ifa_name)) { ifid = if_nametoindex(ifa->ifa_name); break; } ifa = ifa->ifa_next; } freeifaddrs(ifaddr); return ifid; } } // getExternalInterface may need some delay to detect external interface for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) { ifid = getExternalInterface(skip_idx); if (ifid > 0) { return ifid; } if (wait) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); } } return -1; } void waybar::modules::Network::clearIface() { essid_.clear(); ipaddr_.clear(); netmask_.clear(); cidr_ = 0; signal_strength_dbm_ = 0; signal_strength_ = 0; frequency_ = 0; } void waybar::modules::Network::checkNewInterface(struct ifinfomsg *rtif) { auto new_iface = getPreferredIface(rtif->ifi_index); if (new_iface != -1) { ifid_ = new_iface; char ifname[IF_NAMESIZE]; if_indextoname(new_iface, ifname); ifname_ = ifname; getInterfaceAddress(); thread_timer_.wake_up(); } else { ifid_ = -1; dp.emit(); } } int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { auto net = static_cast(data); std::lock_guard lock(net->mutex_); auto nh = nlmsg_hdr(msg); auto ifi = static_cast(NLMSG_DATA(nh)); if (nh->nlmsg_type == RTM_DELADDR) { // Check for valid interface if (ifi->ifi_index == net->ifid_) { net->ipaddr_.clear(); net->netmask_.clear(); net->cidr_ = 0; if (!(ifi->ifi_flags & IFF_RUNNING)) { net->clearIface(); // Check for a new interface and get info net->checkNewInterface(ifi); } else { net->dp.emit(); } return NL_OK; } } else if (nh->nlmsg_type == RTM_NEWLINK || nh->nlmsg_type == RTM_DELLINK) { char ifname[IF_NAMESIZE]; if_indextoname(ifi->ifi_index, ifname); // Check for valid interface if (ifi->ifi_index != net->ifid_ && net->checkInterface(ifi, ifname)) { net->ifname_ = ifname; net->ifid_ = ifi->ifi_index; // Get Iface and WIFI info net->getInterfaceAddress(); net->thread_timer_.wake_up(); return NL_OK; } else if (ifi->ifi_index == net->ifid_ && (!(ifi->ifi_flags & IFF_RUNNING) || !(ifi->ifi_flags & IFF_UP) || !net->checkInterface(ifi, ifname))) { net->clearIface(); // Check for a new interface and get info net->checkNewInterface(ifi); return NL_OK; } } else { char ifname[IF_NAMESIZE]; if_indextoname(ifi->ifi_index, ifname); // Auto detected network can also be assigned here if (ifi->ifi_index != net->ifid_ && net->checkInterface(ifi, ifname)) { // If iface is different, clear data if (ifi->ifi_index != net->ifid_) { net->clearIface(); } net->ifname_ = ifname; net->ifid_ = ifi->ifi_index; } // Check for valid interface if (ifi->ifi_index == net->ifid_) { // Get Iface and WIFI info net->getInterfaceAddress(); net->thread_timer_.wake_up(); return NL_OK; } } return NL_SKIP; } int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) { auto net = static_cast(data); auto gnlh = static_cast(nlmsg_data(nlmsg_hdr(msg))); struct nlattr * tb[NL80211_ATTR_MAX + 1]; struct nlattr * bss[NL80211_BSS_MAX + 1]; struct nla_policy bss_policy[NL80211_BSS_MAX + 1]{}; bss_policy[NL80211_BSS_TSF].type = NLA_U64; bss_policy[NL80211_BSS_FREQUENCY].type = NLA_U32; bss_policy[NL80211_BSS_BSSID].type = NLA_UNSPEC; bss_policy[NL80211_BSS_BEACON_INTERVAL].type = NLA_U16; bss_policy[NL80211_BSS_CAPABILITY].type = NLA_U16; bss_policy[NL80211_BSS_INFORMATION_ELEMENTS].type = NLA_UNSPEC; bss_policy[NL80211_BSS_SIGNAL_MBM].type = NLA_U32; bss_policy[NL80211_BSS_SIGNAL_UNSPEC].type = NLA_U8; bss_policy[NL80211_BSS_STATUS].type = NLA_U32; if (nla_parse( tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), nullptr) < 0) { return NL_SKIP; } if (tb[NL80211_ATTR_BSS] == nullptr) { return NL_SKIP; } if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy) != 0) { return NL_SKIP; } if (!net->associatedOrJoined(bss)) { return NL_SKIP; } net->parseEssid(bss); net->parseSignal(bss); net->parseFreq(bss); return NL_OK; } void waybar::modules::Network::parseEssid(struct nlattr **bss) { if (bss[NL80211_BSS_INFORMATION_ELEMENTS] != nullptr) { auto ies = static_cast(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS])); auto ies_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); const auto hdr_len = 2; while (ies_len > hdr_len && ies[0] != 0) { ies_len -= ies[1] + hdr_len; ies += ies[1] + hdr_len; } if (ies_len > hdr_len && ies_len > ies[1] + hdr_len) { auto essid_begin = ies + hdr_len; auto essid_end = essid_begin + ies[1]; std::string essid_raw; std::copy(essid_begin, essid_end, std::back_inserter(essid_raw)); essid_ = Glib::Markup::escape_text(essid_raw); } } } void waybar::modules::Network::parseSignal(struct nlattr **bss) { if (bss[NL80211_BSS_SIGNAL_MBM] != nullptr) { // signalstrength in dBm from mBm signal_strength_dbm_ = nla_get_s32(bss[NL80211_BSS_SIGNAL_MBM]) / 100; // WiFi-hardware usually operates in the range -90 to -20dBm. const int hardwareMax = -20; const int hardwareMin = -90; const int strength = ((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100; signal_strength_ = std::clamp(strength, 0, 100); } if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) { signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); } } void waybar::modules::Network::parseFreq(struct nlattr **bss) { if (bss[NL80211_BSS_FREQUENCY] != nullptr) { // in MHz frequency_ = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); } } bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) { if (bss[NL80211_BSS_STATUS] == nullptr) { return false; } auto status = nla_get_u32(bss[NL80211_BSS_STATUS]); switch (status) { case NL80211_BSS_STATUS_ASSOCIATED: case NL80211_BSS_STATUS_IBSS_JOINED: case NL80211_BSS_STATUS_AUTHENTICATED: return true; default: return false; } } auto waybar::modules::Network::getInfo() -> void { struct nl_msg *nl_msg = nlmsg_alloc(); if (nl_msg == nullptr) { return; } if (genlmsg_put( nl_msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl80211_id_, 0, NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0) == nullptr || nla_put_u32(nl_msg, NL80211_ATTR_IFINDEX, ifid_) < 0) { nlmsg_free(nl_msg); return; } nl_send_sync(sock_, nl_msg); } // https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495 bool waybar::modules::Network::wildcardMatch(const std::string &pattern, const std::string &text) const { auto P = int(pattern.size()); auto T = int(text.size()); auto p = 0, fallback_p = -1; auto t = 0, fallback_t = -1; while (t < T) { // Wildcard match: if (p < P && pattern[p] == '*') { fallback_p = p++; // starting point after failures fallback_t = t; // starting point after failures } // Simple match: else if (p < P && (pattern[p] == '?' || pattern[p] == text[t])) { p++; t++; } // Failure, fall back just after last matched '*': else if (fallback_p >= 0) { p = fallback_p + 1; // position just after last matched '*" t = ++fallback_t; // re-try to match text from here } // There were no '*' before, so we fail here: else { return false; } } // Consume all '*' at the end of pattern: while (p < P && pattern[p] == '*') p++; return p == P; } waybar-0.9.0/src/modules/pulseaudio.cpp000066400000000000000000000204771360163675500201500ustar00rootroot00000000000000#include "modules/pulseaudio.hpp" waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config) : ALabel(config, "pulseaudio", id, "{volume}%"), mainloop_(nullptr), mainloop_api_(nullptr), context_(nullptr), sink_idx_(0), volume_(0), muted_(false), source_idx_(0), source_volume_(0), source_muted_(false) { mainloop_ = pa_threaded_mainloop_new(); if (mainloop_ == nullptr) { throw std::runtime_error("pa_mainloop_new() failed."); } pa_threaded_mainloop_lock(mainloop_); mainloop_api_ = pa_threaded_mainloop_get_api(mainloop_); context_ = pa_context_new(mainloop_api_, "waybar"); if (context_ == nullptr) { throw std::runtime_error("pa_context_new() failed."); } if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOAUTOSPAWN, nullptr) < 0) { auto err = fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(context_))); throw std::runtime_error(err); } pa_context_set_state_callback(context_, contextStateCb, this); if (pa_threaded_mainloop_start(mainloop_) < 0) { throw std::runtime_error("pa_mainloop_run() failed."); } pa_threaded_mainloop_unlock(mainloop_); event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleScroll)); } waybar::modules::Pulseaudio::~Pulseaudio() { mainloop_api_->quit(mainloop_api_, 0); pa_threaded_mainloop_stop(mainloop_); pa_threaded_mainloop_free(mainloop_); } void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) { auto pa = static_cast(data); switch (pa_context_get_state(c)) { case PA_CONTEXT_TERMINATED: pa->mainloop_api_->quit(pa->mainloop_api_, 0); break; case PA_CONTEXT_READY: pa_context_get_server_info(c, serverInfoCb, data); pa_context_set_subscribe_callback(c, subscribeCb, data); pa_context_subscribe( c, static_cast(static_cast(PA_SUBSCRIPTION_MASK_SINK) | static_cast(PA_SUBSCRIPTION_MASK_SOURCE)), nullptr, nullptr); break; case PA_CONTEXT_FAILED: pa->mainloop_api_->quit(pa->mainloop_api_, 1); break; case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: default: break; } } bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { // change the pulse volume only when no user provided // events are configured if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { return AModule::handleScroll(e); } auto dir = AModule::getScrollDir(e); if (dir == SCROLL_DIR::NONE) { return true; } double volume_tick = static_cast(PA_VOLUME_NORM) / 100; pa_volume_t change = volume_tick; pa_cvolume pa_volume = pa_volume_; // isDouble returns true for integers as well, just in case if (config_["scroll-step"].isDouble()) { change = round(config_["scroll-step"].asDouble() * volume_tick); } if (dir == SCROLL_DIR::UP) { if (volume_ + 1 <= 100) { pa_cvolume_inc(&pa_volume, change); } } else if (dir == SCROLL_DIR::DOWN) { if (volume_ - 1 >= 0) { pa_cvolume_dec(&pa_volume, change); } } pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); return true; } /* * Called when an event we subscribed to occurs. */ void waybar::modules::Pulseaudio::subscribeCb(pa_context * context, pa_subscription_event_type_t type, uint32_t idx, void *data) { unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; unsigned operation = type & PA_SUBSCRIPTION_EVENT_TYPE_MASK; if (operation != PA_SUBSCRIPTION_EVENT_CHANGE) { return; } if (facility == PA_SUBSCRIPTION_EVENT_SINK) { pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data); } else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) { pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data); } } /* * Called in response to a volume change request */ void waybar::modules::Pulseaudio::volumeModifyCb(pa_context *c, int success, void *data) { auto pa = static_cast(data); if (success != 0) { pa_context_get_sink_info_by_index(pa->context_, pa->sink_idx_, sinkInfoCb, data); } } /* * Called when the requested source information is ready. */ void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i, int /*eol*/, void *data) { if (i != nullptr) { auto self = static_cast(data); auto source_volume = static_cast(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM}; self->source_volume_ = std::round(source_volume * 100.0F); self->source_idx_ = i->index; self->source_muted_ = i->mute != 0; self->source_desc_ = i->description; self->source_port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; self->dp.emit(); } } /* * Called when the requested sink information is ready. */ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, int /*eol*/, void * data) { if (i != nullptr) { auto pa = static_cast(data); pa->pa_volume_ = i->volume; float volume = static_cast(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM}; pa->sink_idx_ = i->index; pa->volume_ = std::round(volume * 100.0F); pa->muted_ = i->mute != 0; pa->desc_ = i->description; pa->monitor_ = i->monitor_source_name; pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; pa->dp.emit(); } } /* * Called when the requested information on the server is ready. This is * used to find the default PulseAudio sink. */ void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i, void *data) { pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data); pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data); } static const std::array ports = { "headphones", "speaker", "hdmi", "headset", "handsfree", "portable", "car", "hifi", "phone", }; const std::string waybar::modules::Pulseaudio::getPortIcon() const { std::string nameLC = port_name_; std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { if (nameLC.find(port) != std::string::npos) { return port; } } return port_name_; } auto waybar::modules::Pulseaudio::update() -> void { auto format = format_; std::string format_name = "format"; if (monitor_.find("a2dp_sink") != std::string::npos) { format_name = format_name + "-bluetooth"; label_.get_style_context()->add_class("bluetooth"); } else { label_.get_style_context()->remove_class("bluetooth"); } if (muted_ ) { format_name = format_name + "-muted"; label_.get_style_context()->add_class("muted"); } else { label_.get_style_context()->remove_class("muted"); } format = config_[format_name].isString() ? config_[format_name].asString() : format; // TODO: find a better way to split source/sink std::string format_source = "{volume}%"; if (source_muted_ && config_["format-source-muted"].isString()) { format_source = config_["format-source-muted"].asString(); } else if (!source_muted_ && config_["format-source"].isString()) { format_source = config_["format-source"].asString(); } format_source = fmt::format(format_source, fmt::arg("volume", source_volume_)); label_.set_markup(fmt::format(format, fmt::arg("desc", desc_), fmt::arg("volume", volume_), fmt::arg("format_source", format_source), fmt::arg("icon", getIcon(volume_, getPortIcon())))); getState(volume_); if (tooltipEnabled()) { label_.set_tooltip_text(desc_); } } waybar-0.9.0/src/modules/sni/000077500000000000000000000000001360163675500160515ustar00rootroot00000000000000waybar-0.9.0/src/modules/sni/host.cpp000066400000000000000000000120251360163675500175320ustar00rootroot00000000000000#include "modules/sni/host.hpp" #include #include namespace waybar::modules::SNI { Host::Host(const std::size_t id, const Json::Value& config, const std::function&)>& on_add, const std::function&)>& on_remove) : bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" + std::to_string(id)), object_path_("/StatusNotifierHost/" + std::to_string(id)), bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION, bus_name_, sigc::mem_fun(*this, &Host::busAcquired))), config_(config), on_add_(on_add), on_remove_(on_remove) {} Host::~Host() { if (bus_name_id_ > 0) { Gio::DBus::unwatch_name(bus_name_id_); bus_name_id_ = 0; } if (watcher_id_ > 0) { Gio::DBus::unwatch_name(watcher_id_); watcher_id_ = 0; } g_cancellable_cancel(cancellable_); g_clear_object(&cancellable_); g_clear_object(&watcher_); } void Host::busAcquired(const Glib::RefPtr& conn, Glib::ustring name) { watcher_id_ = Gio::DBus::watch_name(conn, "org.kde.StatusNotifierWatcher", sigc::mem_fun(*this, &Host::nameAppeared), sigc::mem_fun(*this, &Host::nameVanished)); } void Host::nameAppeared(const Glib::RefPtr& conn, const Glib::ustring name, const Glib::ustring& name_owner) { if (cancellable_ != nullptr) { // TODO return; } cancellable_ = g_cancellable_new(); sn_watcher_proxy_new(conn->gobj(), G_DBUS_PROXY_FLAGS_NONE, "org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher", cancellable_, &Host::proxyReady, this); } void Host::nameVanished(const Glib::RefPtr& conn, const Glib::ustring name) { g_cancellable_cancel(cancellable_); g_clear_object(&cancellable_); g_clear_object(&watcher_); items_.clear(); } void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; SnWatcher* watcher = sn_watcher_proxy_new_finish(res, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { spdlog::error("Host: {}", error->message); g_error_free(error); return; } auto host = static_cast(data); host->watcher_ = watcher; if (error != nullptr) { spdlog::error("Host: {}", error->message); g_error_free(error); return; } sn_watcher_call_register_host( host->watcher_, host->object_path_.c_str(), host->cancellable_, &Host::registerHost, data); } void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; sn_watcher_call_register_host_finish(SN_WATCHER(src), res, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { spdlog::error("Host: {}", error->message); g_error_free(error); return; } auto host = static_cast(data); if (error != nullptr) { spdlog::error("Host: {}", error->message); g_error_free(error); return; } g_signal_connect(host->watcher_, "item-registered", G_CALLBACK(&Host::itemRegistered), data); g_signal_connect(host->watcher_, "item-unregistered", G_CALLBACK(&Host::itemUnregistered), data); auto items = sn_watcher_dup_registered_items(host->watcher_); if (items != nullptr) { for (uint32_t i = 0; items[i] != nullptr; i += 1) { host->addRegisteredItem(items[i]); } } g_strfreev(items); } void Host::itemRegistered(SnWatcher* watcher, const gchar* service, gpointer data) { auto host = static_cast(data); host->addRegisteredItem(service); } void Host::itemUnregistered(SnWatcher* watcher, const gchar* service, gpointer data) { auto host = static_cast(data); auto [bus_name, object_path] = host->getBusNameAndObjectPath(service); for (auto it = host->items_.begin(); it != host->items_.end(); ++it) { if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) { host->on_remove_(*it); host->items_.erase(it); break; } } } std::tuple Host::getBusNameAndObjectPath(const std::string service) { auto it = service.find('/'); if (it != std::string::npos) { return {service.substr(0, it), service.substr(it)}; } return {service, "/StatusNotifierItem"}; } void Host::addRegisteredItem(std::string service) { std::string bus_name, object_path; std::tie(bus_name, object_path) = getBusNameAndObjectPath(service); auto it = std::find_if(items_.begin(), items_.end(), [&bus_name, &object_path](const auto& item) { return bus_name == item->bus_name && object_path == item->object_path; }); if (it == items_.end()) { items_.emplace_back(new Item(bus_name, object_path, config_)); on_add_(items_.back()); } } } // namespace waybar::modules::SNI waybar-0.9.0/src/modules/sni/item.cpp000066400000000000000000000312131360163675500175130ustar00rootroot00000000000000#include "modules/sni/item.hpp" #include #include template <> struct fmt::formatter : formatter { template auto format(const Glib::ustring& value, FormatContext& ctx) { return formatter::format(value, ctx); } }; template <> struct fmt::formatter : formatter { bool is_printable(const Glib::VariantBase& value) { auto type = value.get_type_string(); /* Print only primitive (single character excluding 'v') and short complex types */ return (type.length() == 1 && islower(type[0]) && type[0] != 'v') || value.get_size() <= 32; } template auto format(const Glib::VariantBase& value, FormatContext& ctx) { if (is_printable(value)) { return formatter::format(value.print(), ctx); } else { return formatter::format(value.get_type_string(), ctx); } } }; namespace waybar::modules::SNI { static const Glib::ustring SNI_INTERFACE_NAME = sn_item_interface_info()->name; static const unsigned UPDATE_DEBOUNCE_TIME = 10; Item::Item(const std::string& bn, const std::string& op, const Json::Value& config) : bus_name(bn), object_path(op), icon_size(16), effective_icon_size(0), icon_theme(Gtk::IconTheme::create()), update_pending_(false) { if (config["icon-size"].isUInt()) { icon_size = config["icon-size"].asUInt(); } event_box.add(image); event_box.add_events(Gdk::BUTTON_PRESS_MASK); event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick)); cancellable_ = Gio::Cancellable::create(); auto interface = Glib::wrap(sn_item_interface_info(), true); Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SESSION, bus_name, object_path, SNI_INTERFACE_NAME, sigc::mem_fun(*this, &Item::proxyReady), cancellable_, interface); } void Item::proxyReady(Glib::RefPtr& result) { try { this->proxy_ = Gio::DBus::Proxy::create_for_bus_finish(result); /* Properties are already cached during object creation */ auto cached_properties = this->proxy_->get_cached_property_names(); for (const auto& name : cached_properties) { Glib::VariantBase value; this->proxy_->get_cached_property(value, name); setProperty(name, value); } this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal)); if (this->id.empty() || this->category.empty() || this->status.empty()) { spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path); return; } this->updateImage(); // this->event_box.set_tooltip_text(this->title); } catch (const Glib::Error& err) { spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); } catch (const std::exception& err) { spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); } } template T get_variant(Glib::VariantBase& value) { return Glib::VariantBase::cast_dynamic>(value).get(); } void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { try { spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value); if (name == "Category") { category = get_variant(value); } else if (name == "Id") { id = get_variant(value); } else if (name == "Title") { title = get_variant(value); } else if (name == "Status") { status = get_variant(value); } else if (name == "WindowId") { window_id = get_variant(value); } else if (name == "IconName") { icon_name = get_variant(value); } else if (name == "IconPixmap") { icon_pixmap = this->extractPixBuf(value.gobj()); } else if (name == "OverlayIconName") { overlay_icon_name = get_variant(value); } else if (name == "OverlayIconPixmap") { // TODO: overlay_icon_pixmap } else if (name == "AttentionIconName") { attention_icon_name = get_variant(value); } else if (name == "AttentionIconPixmap") { // TODO: attention_icon_pixmap } else if (name == "AttentionMovieName") { attention_movie_name = get_variant(value); } else if (name == "ToolTip") { // TODO: tooltip } else if (name == "IconThemePath") { icon_theme_path = get_variant(value); if (!icon_theme_path.empty()) { icon_theme->set_search_path({icon_theme_path}); } } else if (name == "Menu") { menu = get_variant(value); makeMenu(); } else if (name == "ItemIsMenu") { item_is_menu = get_variant(value); } } catch (const Glib::Error& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", id.empty() ? bus_name : id, name, value, err.what()); } catch (const std::exception& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", id.empty() ? bus_name : id, name, value, err.what()); } } void Item::getUpdatedProperties() { update_pending_ = false; auto params = Glib::VariantContainerBase::create_tuple( {Glib::Variant::create(SNI_INTERFACE_NAME)}); proxy_->call("org.freedesktop.DBus.Properties.GetAll", sigc::mem_fun(*this, &Item::processUpdatedProperties), params); }; void Item::processUpdatedProperties(Glib::RefPtr& _result) { try { auto result = proxy_->call_finish(_result); // extract "a{sv}" from VariantContainerBase Glib::Variant> properties_variant; result.get_child(properties_variant); auto properties = properties_variant.get(); for (const auto& [name, value] : properties) { Glib::VariantBase old_value; proxy_->get_cached_property(old_value, name); if (!old_value || !value.equal(old_value)) { proxy_->set_cached_property(name, value); setProperty(name, const_cast(value)); } } this->updateImage(); // this->event_box.set_tooltip_text(this->title); } catch (const Glib::Error& err) { spdlog::warn("Failed to update properties: {}", err.what()); } catch (const std::exception& err) { spdlog::warn("Failed to update properties: {}", err.what()); } } void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, const Glib::VariantContainerBase& arguments) { spdlog::trace("Tray item '{}' got signal {}", id, signal_name); if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) { /* Debounce signals and schedule update of all properties. * Based on behavior of Plasma dataengine for StatusNotifierItem. */ update_pending_ = true; Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties), UPDATE_DEBOUNCE_TIME); } } static void pixbuf_data_deleter(const guint8* data) { g_free((void*)data); } Glib::RefPtr Item::extractPixBuf(GVariant* variant) { GVariantIter* it; g_variant_get(variant, "a(iiay)", &it); if (it == nullptr) { return Glib::RefPtr{}; } GVariant* val; gint lwidth = 0; gint lheight = 0; gint width; gint height; guchar* array = nullptr; while (g_variant_iter_loop(it, "(ii@ay)", &width, &height, &val)) { if (width > 0 && height > 0 && val != nullptr && width * height > lwidth * lheight) { auto size = g_variant_get_size(val); /* Sanity check */ if (size == 4U * width * height) { /* Find the largest image */ gconstpointer data = g_variant_get_data(val); if (data != nullptr) { if (array != nullptr) { g_free(array); } array = static_cast(g_memdup(data, size)); lwidth = width; lheight = height; } } } } g_variant_iter_free(it); if (array != nullptr) { /* argb to rgba */ for (uint32_t i = 0; i < 4U * lwidth * lheight; i += 4) { guchar alpha = array[i]; array[i] = array[i + 1]; array[i + 1] = array[i + 2]; array[i + 2] = array[i + 3]; array[i + 3] = alpha; } return Gdk::Pixbuf::create_from_data(array, Gdk::Colorspace::COLORSPACE_RGB, true, 8, lwidth, lheight, 4 * lwidth, &pixbuf_data_deleter); } return Glib::RefPtr{}; } void Item::updateImage() { image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU); image.set_pixel_size(icon_size); if (!icon_name.empty()) { try { // Try to find icons specified by path and filename #ifdef FILESYSTEM_EXPERIMENTAL if (std::experimental::filesystem::exists(icon_name)) { #else if (std::filesystem::exists(icon_name)) { #endif auto pixbuf = Gdk::Pixbuf::create_from_file(icon_name); if (pixbuf->gobj() != nullptr) { // An icon specified by path and filename may be the wrong size for // the tray pixbuf = pixbuf->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR); image.set(pixbuf); } } else { image.set(getIconByName(icon_name, icon_size)); } } catch (Glib::Error& e) { spdlog::error("Item '{}': {}", id, static_cast(e.what())); } } else if (icon_pixmap) { // An icon extracted may be the wrong size for the tray icon_pixmap = icon_pixmap->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR); image.set(icon_pixmap); } } Glib::RefPtr Item::getIconByName(const std::string& name, int request_size) { int tmp_size = 0; icon_theme->rescan_if_needed(); auto sizes = icon_theme->get_icon_sizes(name.c_str()); for (auto const& size : sizes) { // -1 == scalable if (size == request_size || size == -1) { tmp_size = request_size; break; } else if (size < request_size) { tmp_size = size; } else if (size > tmp_size && tmp_size > 0) { tmp_size = request_size; break; } } if (tmp_size == 0) { tmp_size = request_size; } if (!icon_theme_path.empty() && icon_theme->lookup_icon( name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE)) { return icon_theme->load_icon( name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } Glib::RefPtr default_theme = Gtk::IconTheme::get_default(); default_theme->rescan_if_needed(); return default_theme->load_icon( name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } void Item::onMenuDestroyed(Item* self, GObject* old_menu_pointer) { if (old_menu_pointer == reinterpret_cast(self->dbus_menu)) { self->gtk_menu = nullptr; self->dbus_menu = nullptr; } } void Item::makeMenu() { if (gtk_menu == nullptr && !menu.empty()) { dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data()); if (dbus_menu != nullptr) { g_object_ref_sink(G_OBJECT(dbus_menu)); g_object_weak_ref(G_OBJECT(dbus_menu), (GWeakNotify)onMenuDestroyed, this); gtk_menu = Glib::wrap(GTK_MENU(dbus_menu)); gtk_menu->attach_to_widget(event_box); } } } bool Item::handleClick(GdkEventButton* const& ev) { auto parameters = Glib::VariantContainerBase::create_tuple( {Glib::Variant::create(ev->x), Glib::Variant::create(ev->y)}); if ((ev->button == 1 && item_is_menu) || ev->button == 3) { makeMenu(); if (gtk_menu != nullptr) { #if GTK_CHECK_VERSION(3, 22, 0) gtk_menu->popup_at_pointer(reinterpret_cast(ev)); #else gtk_menu->popup(ev->button, ev->time); #endif return true; } else { proxy_->call("ContextMenu", parameters); return true; } } else if (ev->button == 1) { proxy_->call("Activate", parameters); return true; } else if (ev->button == 2) { proxy_->call("SecondaryActivate", parameters); return true; } return false; } } // namespace waybar::modules::SNI waybar-0.9.0/src/modules/sni/tray.cpp000066400000000000000000000023001360163675500175270ustar00rootroot00000000000000#include "modules/sni/tray.hpp" #include namespace waybar::modules::SNI { Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) : AModule(config, "tray", id), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), watcher_(nb_hosts_), host_(nb_hosts_, config, std::bind(&Tray::onAdd, this, std::placeholders::_1), std::bind(&Tray::onRemove, this, std::placeholders::_1)) { spdlog::warn( "For a functional tray you must have libappindicator-* installed and export " "XDG_CURRENT_DESKTOP=Unity"); box_.set_name("tray"); event_box_.add(box_); if (!id.empty()) { box_.get_style_context()->add_class(id); } if (config_["spacing"].isUInt()) { box_.set_spacing(config_["spacing"].asUInt()); } nb_hosts_ += 1; dp.emit(); } void Tray::onAdd(std::unique_ptr& item) { box_.pack_start(item->event_box); dp.emit(); } void Tray::onRemove(std::unique_ptr& item) { box_.remove(item->event_box); dp.emit(); } auto Tray::update() -> void { if (box_.get_children().empty()) { box_.hide(); } else { box_.show_all(); } } } // namespace waybar::modules::SNI waybar-0.9.0/src/modules/sni/watcher.cpp000066400000000000000000000166371360163675500202270ustar00rootroot00000000000000#include "modules/sni/watcher.hpp" #include using namespace waybar::modules::SNI; Watcher::Watcher(std::size_t id) : bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION, "org.kde.StatusNotifierWatcher", sigc::mem_fun(*this, &Watcher::busAcquired), Gio::DBus::SlotNameAcquired(), Gio::DBus::SlotNameLost(), Gio::DBus::BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE)), watcher_id_(id), watcher_(sn_watcher_skeleton_new()) {} Watcher::~Watcher() { if (hosts_ != nullptr) { g_slist_free_full(hosts_, gfWatchFree); hosts_ = nullptr; } if (items_ != nullptr) { g_slist_free_full(items_, gfWatchFree); items_ = nullptr; } auto iface = G_DBUS_INTERFACE_SKELETON(watcher_); g_dbus_interface_skeleton_unexport(iface); } void Watcher::busAcquired(const Glib::RefPtr& conn, Glib::ustring name) { GError* error = nullptr; g_dbus_interface_skeleton_export( G_DBUS_INTERFACE_SKELETON(watcher_), conn->gobj(), "/StatusNotifierWatcher", &error); if (error != nullptr) { // Don't print an error when a watcher is already present if (error->code != 2) { spdlog::error("Watcher {}: {}", watcher_id_, error->message); } g_error_free(error); return; } g_signal_connect_swapped( watcher_, "handle-register-item", G_CALLBACK(&Watcher::handleRegisterItem), this); g_signal_connect_swapped( watcher_, "handle-register-host", G_CALLBACK(&Watcher::handleRegisterHost), this); } gboolean Watcher::handleRegisterHost(Watcher* obj, GDBusMethodInvocation* invocation, const gchar* service) { const gchar* bus_name = service; const gchar* object_path = "/StatusNotifierHost"; if (*service == '/') { bus_name = g_dbus_method_invocation_get_sender(invocation); object_path = service; } if (g_dbus_is_name(bus_name) == FALSE) { g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "D-Bus bus name '%s' is not valid", bus_name); return TRUE; } auto watch = gfWatchFind(obj->hosts_, bus_name, object_path); if (watch != nullptr) { g_dbus_method_invocation_return_error( invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Status Notifier Host with bus name '%s' and object path '%s' is already registered", bus_name, object_path); return TRUE; } watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj); obj->hosts_ = g_slist_prepend(obj->hosts_, watch); if (!sn_watcher_get_is_host_registered(obj->watcher_)) { sn_watcher_set_is_host_registered(obj->watcher_, TRUE); sn_watcher_emit_host_registered(obj->watcher_); } sn_watcher_complete_register_host(obj->watcher_, invocation); return TRUE; } gboolean Watcher::handleRegisterItem(Watcher* obj, GDBusMethodInvocation* invocation, const gchar* service) { const gchar* bus_name = service; const gchar* object_path = "/StatusNotifierItem"; if (*service == '/') { bus_name = g_dbus_method_invocation_get_sender(invocation); object_path = service; } if (g_dbus_is_name(bus_name) == FALSE) { g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "D-Bus bus name '%s' is not valid", bus_name); return TRUE; } auto watch = gfWatchFind(obj->items_, bus_name, object_path); if (watch != nullptr) { g_warning("Status Notifier Item with bus name '%s' and object path '%s' is already registered", bus_name, object_path); sn_watcher_complete_register_item(obj->watcher_, invocation); return TRUE; } watch = gfWatchNew(GF_WATCH_TYPE_ITEM, service, bus_name, object_path, obj); obj->items_ = g_slist_prepend(obj->items_, watch); obj->updateRegisteredItems(obj->watcher_); gchar* tmp = g_strdup_printf("%s%s", bus_name, object_path); sn_watcher_emit_item_registered(obj->watcher_, tmp); g_free(tmp); sn_watcher_complete_register_item(obj->watcher_, invocation); return TRUE; } Watcher::GfWatch* Watcher::gfWatchFind(GSList* list, const gchar* bus_name, const gchar* object_path) { for (GSList* l = list; l != nullptr; l = g_slist_next(l)) { auto watch = static_cast(l->data); if (g_strcmp0(watch->bus_name, bus_name) == 0 && g_strcmp0(watch->object_path, object_path) == 0) { return watch; } } return nullptr; } void Watcher::gfWatchFree(gpointer data) { auto watch = static_cast(data); if (watch->watch_id > 0) { g_bus_unwatch_name(watch->watch_id); } g_free(watch->service); g_free(watch->bus_name); g_free(watch->object_path); g_free(watch); } Watcher::GfWatch* Watcher::gfWatchNew(GfWatchType type, const gchar* service, const gchar* bus_name, const gchar* object_path, Watcher* watcher) { GfWatch* watch = g_new0(GfWatch, 1); watch->type = type; watch->watcher = watcher; watch->service = g_strdup(service); watch->bus_name = g_strdup(bus_name); watch->object_path = g_strdup(object_path); watch->watch_id = g_bus_watch_name(G_BUS_TYPE_SESSION, bus_name, G_BUS_NAME_WATCHER_FLAGS_NONE, nullptr, &Watcher::nameVanished, watch, nullptr); return watch; } void Watcher::nameVanished(GDBusConnection* connection, const char* name, gpointer data) { auto watch = static_cast(data); if (watch->type == GF_WATCH_TYPE_HOST) { watch->watcher->hosts_ = g_slist_remove(watch->watcher->hosts_, watch); if (watch->watcher->hosts_ == nullptr) { sn_watcher_set_is_host_registered(watch->watcher->watcher_, FALSE); sn_watcher_emit_host_registered(watch->watcher->watcher_); } } else if (watch->type == GF_WATCH_TYPE_ITEM) { watch->watcher->items_ = g_slist_remove(watch->watcher->items_, watch); watch->watcher->updateRegisteredItems(watch->watcher->watcher_); gchar* tmp = g_strdup_printf("%s%s", watch->bus_name, watch->object_path); sn_watcher_emit_item_unregistered(watch->watcher->watcher_, tmp); g_free(tmp); } } void Watcher::updateRegisteredItems(SnWatcher* obj) { GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE("as")); for (GSList* l = items_; l != nullptr; l = g_slist_next(l)) { auto watch = static_cast(l->data); gchar* item = g_strdup_printf("%s%s", watch->bus_name, watch->object_path); g_variant_builder_add(&builder, "s", item); g_free(item); } GVariant* variant = g_variant_builder_end(&builder); const gchar** items = g_variant_get_strv(variant, nullptr); sn_watcher_set_registered_items(obj, items); g_variant_unref(variant); g_free(items); } waybar-0.9.0/src/modules/sway/000077500000000000000000000000001360163675500162435ustar00rootroot00000000000000waybar-0.9.0/src/modules/sway/ipc/000077500000000000000000000000001360163675500170165ustar00rootroot00000000000000waybar-0.9.0/src/modules/sway/ipc/client.cpp000066400000000000000000000077511360163675500210120ustar00rootroot00000000000000#include "modules/sway/ipc/client.hpp" #include namespace waybar::modules::sway { Ipc::Ipc() { const std::string& socketPath = getSocketPath(); fd_ = open(socketPath); fd_event_ = open(socketPath); } Ipc::~Ipc() { // To fail the IPC header write(fd_, "close-sway-ipc", 14); write(fd_event_, "close-sway-ipc", 14); if (fd_ > 0) { close(fd_); fd_ = -1; } if (fd_event_ > 0) { close(fd_event_); fd_event_ = -1; } } const std::string Ipc::getSocketPath() const { const char* env = getenv("SWAYSOCK"); if (env != nullptr) { return std::string(env); } std::string str; { std::string str_buf; FILE* in; char buf[512] = {0}; if ((in = popen("sway --get-socketpath 2>/dev/null", "r")) == nullptr) { throw std::runtime_error("Failed to get socket path"); } while (fgets(buf, sizeof(buf), in) != nullptr) { str_buf.append(buf, sizeof(buf)); } pclose(in); str = str_buf; if (str.empty()) { throw std::runtime_error("Socket path is empty"); } } if (str.back() == '\n') { str.pop_back(); } return str; } int Ipc::open(const std::string& socketPath) const { int32_t fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { throw std::runtime_error("Unable to open Unix socket"); } (void)fcntl(fd, F_SETFD, FD_CLOEXEC); struct sockaddr_un addr; memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); addr.sun_path[sizeof(addr.sun_path) - 1] = 0; int l = sizeof(struct sockaddr_un); if (::connect(fd, reinterpret_cast(&addr), l) == -1) { throw std::runtime_error("Unable to connect to Sway"); } return fd; } struct Ipc::ipc_response Ipc::recv(int fd) { std::string header; header.resize(ipc_header_size_); auto data32 = reinterpret_cast(header.data() + ipc_magic_.size()); size_t total = 0; while (total < ipc_header_size_) { auto res = ::recv(fd, header.data() + total, ipc_header_size_ - total, 0); if (fd_event_ == -1 || fd_ == -1) { // IPC is closed so just return an empty response return {0, 0, ""}; } if (res <= 0) { throw std::runtime_error("Unable to receive IPC header"); } total += res; } auto magic = std::string(header.data(), header.data() + ipc_magic_.size()); if (magic != ipc_magic_) { throw std::runtime_error("Invalid IPC magic"); } total = 0; std::string payload; payload.resize(data32[0]); while (total < data32[0]) { auto res = ::recv(fd, payload.data() + total, data32[0] - total, 0); if (res < 0) { if (errno == EINTR || errno == EAGAIN) { continue; } throw std::runtime_error("Unable to receive IPC payload"); } total += res; } return {data32[0], data32[1], &payload.front()}; } struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& payload) { std::string header; header.resize(ipc_header_size_); auto data32 = reinterpret_cast(header.data() + ipc_magic_.size()); memcpy(header.data(), ipc_magic_.c_str(), ipc_magic_.size()); data32[0] = payload.size(); data32[1] = type; if (::send(fd, header.data(), ipc_header_size_, 0) == -1) { throw std::runtime_error("Unable to send IPC header"); } if (::send(fd, payload.c_str(), payload.size(), 0) == -1) { throw std::runtime_error("Unable to send IPC payload"); } return Ipc::recv(fd); } void Ipc::sendCmd(uint32_t type, const std::string& payload) { std::lock_guard lock(mutex_); const auto res = Ipc::send(fd_, type, payload); signal_cmd.emit(res); } void Ipc::subscribe(const std::string& payload) { auto res = Ipc::send(fd_event_, IPC_SUBSCRIBE, payload); if (res.payload != "{\"success\": true}") { throw std::runtime_error("Unable to subscribe ipc event"); } } void Ipc::handleEvent() { const auto res = Ipc::recv(fd_event_); signal_event.emit(res); } } // namespace waybar::modules::sway waybar-0.9.0/src/modules/sway/mode.cpp000066400000000000000000000025221360163675500176740ustar00rootroot00000000000000#include "modules/sway/mode.hpp" #include namespace waybar::modules::sway { Mode::Mode(const std::string& id, const Json::Value& config) : ALabel(config, "mode", id, "{}", 0, true) { ipc_.subscribe(R"(["mode"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Mode::onEvent)); // Launch worker worker(); dp.emit(); } void Mode::onEvent(const struct Ipc::ipc_response& res) { try { std::lock_guard lock(mutex_); auto payload = parser_.parse(res.payload); if (payload["change"] != "default") { if (payload["pango_markup"].asBool()) { mode_ = payload["change"].asString(); } else { mode_ = Glib::Markup::escape_text(payload["change"].asString()); } } else { mode_.clear(); } dp.emit(); } catch (const std::exception& e) { spdlog::error("Mode: {}", e.what()); } } void Mode::worker() { thread_ = [this] { try { ipc_.handleEvent(); } catch (const std::exception& e) { spdlog::error("Mode: {}", e.what()); } }; } auto Mode::update() -> void { if (mode_.empty()) { event_box_.hide(); } else { label_.set_markup(fmt::format(format_, mode_)); if (tooltipEnabled()) { label_.set_tooltip_text(mode_); } event_box_.show(); } } } // namespace waybar::modules::sway waybar-0.9.0/src/modules/sway/window.cpp000066400000000000000000000070151360163675500202610ustar00rootroot00000000000000#include "modules/sway/window.hpp" #include namespace waybar::modules::sway { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) { ipc_.subscribe(R"(["window","workspace"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd)); // Get Initial focused window getTree(); // Launch worker worker(); } void Window::onEvent(const struct Ipc::ipc_response& res) { getTree(); } void Window::onCmd(const struct Ipc::ipc_response& res) { try { std::lock_guard lock(mutex_); auto payload = parser_.parse(res.payload); auto output = payload["output"].isString() ? payload["output"].asString() : ""; std::tie(app_nb_, windowId_, window_, app_id_) = getFocusedNode(payload["nodes"], output); dp.emit(); } catch (const std::exception& e) { spdlog::error("Window: {}", e.what()); } } void Window::worker() { thread_ = [this] { try { ipc_.handleEvent(); } catch (const std::exception& e) { spdlog::error("Window: {}", e.what()); } }; } auto Window::update() -> void { if (!old_app_id_.empty()) { bar_.window.get_style_context()->remove_class(old_app_id_); } if (app_nb_ == 0) { bar_.window.get_style_context()->remove_class("solo"); if (!bar_.window.get_style_context()->has_class("empty")) { bar_.window.get_style_context()->add_class("empty"); } } else if (app_nb_ == 1) { bar_.window.get_style_context()->remove_class("empty"); if (!bar_.window.get_style_context()->has_class("solo")) { bar_.window.get_style_context()->add_class("solo"); } if (!app_id_.empty() && !bar_.window.get_style_context()->has_class(app_id_)) { bar_.window.get_style_context()->add_class(app_id_); old_app_id_ = app_id_; } } else { bar_.window.get_style_context()->remove_class("solo"); bar_.window.get_style_context()->remove_class("empty"); } label_.set_markup(fmt::format(format_, window_)); if (tooltipEnabled()) { label_.set_tooltip_text(window_); } } std::tuple Window::getFocusedNode( const Json::Value& nodes, std::string& output) { for (auto const& node : nodes) { if (node["output"].isString()) { output = node["output"].asString(); } if (node["focused"].asBool() && (node["type"] == "con" || node["type"] == "floating_con")) { if ((!config_["all-outputs"].asBool() && output == bar_.output->name) || config_["all-outputs"].asBool()) { auto app_id = node["app_id"].isString() ? node["app_id"].asString() : node["window_properties"]["instance"].asString(); return {nodes.size(), node["id"].asInt(), Glib::Markup::escape_text(node["name"].asString()), app_id}; } } auto [nb, id, name, app_id] = getFocusedNode(node["nodes"], output); if (id > -1 && !name.empty()) { return {nb, id, name, app_id}; } // Search for floating node std::tie(nb, id, name, app_id) = getFocusedNode(node["floating_nodes"], output); if (id > -1 && !name.empty()) { return {nb, id, name, app_id}; } } return {0, -1, "", ""}; } void Window::getTree() { try { ipc_.sendCmd(IPC_GET_TREE); } catch (const std::exception& e) { spdlog::error("Window: {}", e.what()); } } } // namespace waybar::modules::sway waybar-0.9.0/src/modules/sway/workspaces.cpp000066400000000000000000000234071360163675500211360ustar00rootroot00000000000000#include "modules/sway/workspaces.hpp" #include namespace waybar::modules::sway { Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), bar_(bar), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); } event_box_.add(box_); ipc_.subscribe(R"(["workspace"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd)); ipc_.sendCmd(IPC_GET_WORKSPACES); if (config["enable-bar-scroll"].asBool()) { auto &window = const_cast(bar_).window; window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll)); } // Launch worker worker(); } void Workspaces::onEvent(const struct Ipc::ipc_response &res) { try { ipc_.sendCmd(IPC_GET_WORKSPACES); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } } void Workspaces::onCmd(const struct Ipc::ipc_response &res) { if (res.type == IPC_GET_WORKSPACES) { try { { std::lock_guard lock(mutex_); auto payload = parser_.parse(res.payload); workspaces_.clear(); std::copy_if(payload.begin(), payload.end(), std::back_inserter(workspaces_), [&](const auto &workspace) { return !config_["all-outputs"].asBool() ? workspace["output"].asString() == bar_.output->name : true; }); // adding persistent workspaces (as per the config file) if (config_["persistent_workspaces"].isObject()) { const Json::Value & p_workspaces = config_["persistent_workspaces"]; const std::vector p_workspaces_names = p_workspaces.getMemberNames(); for (const std::string &p_w_name : p_workspaces_names) { const Json::Value &p_w = p_workspaces[p_w_name]; auto it = std::find_if(payload.begin(), payload.end(), [&p_w_name](const Json::Value &node) { return node["name"].asString() == p_w_name; }); if (it != payload.end()) { continue; // already displayed by some bar } if (p_w.isArray() && !p_w.empty()) { // Adding to target outputs for (const Json::Value &output : p_w) { if (output.asString() == bar_.output->name) { Json::Value v; v["name"] = p_w_name; v["target_output"] = bar_.output->name; workspaces_.emplace_back(std::move(v)); break; } } } else { // Adding to all outputs Json::Value v; v["name"] = p_w_name; v["target_output"] = ""; workspaces_.emplace_back(std::move(v)); } } std::sort(workspaces_.begin(), workspaces_.end(), [](const Json::Value &lhs, const Json::Value &rhs) { if (lhs["name"].isInt() && rhs["name"].isInt()) { return lhs["name"].asInt() < rhs["name"].asInt(); } return lhs["name"].asString() < rhs["name"].asString(); }); } } dp.emit(); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } } } void Workspaces::worker() { thread_ = [this] { try { ipc_.handleEvent(); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } }; } bool Workspaces::filterButtons() { bool needReorder = false; for (auto it = buttons_.begin(); it != buttons_.end();) { auto ws = std::find_if(workspaces_.begin(), workspaces_.end(), [it](const auto &node) { return node["name"].asString() == it->first; }); if (ws == workspaces_.end() || (!config_["all-outputs"].asBool() && (*ws)["output"].asString() != bar_.output->name)) { it = buttons_.erase(it); needReorder = true; } else { ++it; } } return needReorder; } auto Workspaces::update() -> void { std::lock_guard lock(mutex_); bool needReorder = filterButtons(); for (auto it = workspaces_.begin(); it != workspaces_.end(); ++it) { auto bit = buttons_.find((*it)["name"].asString()); if (bit == buttons_.end()) { needReorder = true; } auto &button = bit == buttons_.end() ? addButton(*it) : bit->second; if ((*it)["focused"].asBool()) { button.get_style_context()->add_class("focused"); } else { button.get_style_context()->remove_class("focused"); } if ((*it)["visible"].asBool()) { button.get_style_context()->add_class("visible"); } else { button.get_style_context()->remove_class("visible"); } if ((*it)["urgent"].asBool()) { button.get_style_context()->add_class("urgent"); } else { button.get_style_context()->remove_class("urgent"); } if ((*it)["target_output"].isString()) { button.get_style_context()->add_class("persistent"); } else { button.get_style_context()->remove_class("persistent"); } if (needReorder) { box_.reorder_child(button, it - workspaces_.begin()); } std::string output = getIcon((*it)["name"].asString(), *it); if (config_["format"].isString()) { auto format = config_["format"].asString(); output = fmt::format(format, fmt::arg("icon", output), fmt::arg("name", trimWorkspaceName((*it)["name"].asString())), fmt::arg("index", (*it)["num"].asString())); } if (!config_["disable-markup"].asBool()) { static_cast(button.get_children()[0])->set_markup(output); } else { button.set_label(output); } onButtonReady(*it, button); } } Gtk::Button &Workspaces::addButton(const Json::Value &node) { auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString()); auto &&button = pair.first->second; box_.pack_start(button, false, false, 0); button.set_relief(Gtk::RELIEF_NONE); button.signal_clicked().connect([this, node] { try { if (node["target_output"].isString()) { ipc_.sendCmd( IPC_COMMAND, fmt::format("workspace \"{}\"; move workspace to output \"{}\"; workspace \"{}\"", node["name"].asString(), node["target_output"].asString(), node["name"].asString())); } else { ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", node["name"].asString())); } } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } }); return button; } std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) { std::vector keys = {name, "urgent", "focused", "visible", "default"}; for (auto const &key : keys) { if (key == "focused" || key == "visible" || key == "urgent") { if (config_["format-icons"][key].isString() && node[key].asBool()) { return config_["format-icons"][key].asString(); } } else if (config_["format-icons"][key].isString()) { return config_["format-icons"][key].asString(); } } return name; } bool Workspaces::handleScroll(GdkEventScroll *e) { auto dir = AModule::getScrollDir(e); if (dir == SCROLL_DIR::NONE) { return true; } std::string name; { std::lock_guard lock(mutex_); auto it = std::find_if(workspaces_.begin(), workspaces_.end(), [](const auto &workspace) { return workspace["focused"].asBool(); }); if (it == workspaces_.end()) { return true; } if (dir == SCROLL_DIR::DOWN || dir == SCROLL_DIR::RIGHT) { name = getCycleWorkspace(it, false); } else if (dir == SCROLL_DIR::UP || dir == SCROLL_DIR::LEFT) { name = getCycleWorkspace(it, true); } else { return true; } if (name == (*it)["name"].asString()) { return true; } } try { ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name)); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } return true; } const std::string Workspaces::getCycleWorkspace(std::vector::iterator it, bool prev) const { if (prev && it == workspaces_.begin() && !config_["disable-scroll-wraparound"].asBool()) { return (*(--workspaces_.end()))["name"].asString(); } if (prev && it != workspaces_.begin()) --it; else if (!prev && it != workspaces_.end()) ++it; if (!prev && it == workspaces_.end()) { if (config_["disable-scroll-wraparound"].asBool()) { --it; } else { return (*(workspaces_.begin()))["name"].asString(); } } return (*it)["name"].asString(); } std::string Workspaces::trimWorkspaceName(std::string name) { std::size_t found = name.find(':'); if (found != std::string::npos) { return name.substr(found + 1); } return name; } void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) { if (config_["current-only"].asBool()) { if (node["focused"].asBool()) { button.show(); } else { button.hide(); } } else { button.show(); } } } // namespace waybar::modules::sway waybar-0.9.0/src/modules/temperature.cpp000066400000000000000000000043141360163675500203230ustar00rootroot00000000000000#include "modules/temperature.hpp" waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config) : ALabel(config, "temperature", id, "{temperatureC}°C", 10) { if (config_["hwmon-path"].isString()) { file_path_ = config_["hwmon-path"].asString(); } else { auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); } std::ifstream temp(file_path_); if (!temp.is_open()) { throw std::runtime_error("Can't open " + file_path_); } thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); }; } auto waybar::modules::Temperature::update() -> void { auto [temperature_c, temperature_f] = getTemperature(); auto critical = isCritical(temperature_c); auto format = format_; if (critical) { format = config_["format-critical"].isString() ? config_["format-critical"].asString() : format; label_.get_style_context()->add_class("critical"); } else { label_.get_style_context()->remove_class("critical"); } auto max_temp = config_["critical-threshold"].isInt() ? config_["critical-threshold"].asInt() : 0; label_.set_markup(fmt::format(format, fmt::arg("temperatureC", temperature_c), fmt::arg("temperatureF", temperature_f), fmt::arg("icon", getIcon(temperature_c, "", max_temp)))); } std::tuple waybar::modules::Temperature::getTemperature() { std::ifstream temp(file_path_); if (!temp.is_open()) { throw std::runtime_error("Can't open " + file_path_); } std::string line; if (temp.good()) { getline(temp, line); } temp.close(); auto temperature_c = std::strtol(line.c_str(), nullptr, 10) / 1000.0; auto temperature_f = temperature_c * 1.8 + 32; std::tuple temperatures(std::round(temperature_c), std::round(temperature_f)); return temperatures; } bool waybar::modules::Temperature::isCritical(uint16_t temperature_c) { return config_["critical-threshold"].isInt() && temperature_c >= config_["critical-threshold"].asInt(); } waybar-0.9.0/subprojects/000077500000000000000000000000001360163675500153645ustar00rootroot00000000000000waybar-0.9.0/subprojects/fmt.wrap000066400000000000000000000006231360163675500170460ustar00rootroot00000000000000[wrap-file] directory = fmt-5.3.0 source_url = https://github.com/fmtlib/fmt/archive/5.3.0.tar.gz source_filename = fmt-5.3.0.tar.gz source_hash = defa24a9af4c622a7134076602070b45721a43c51598c8456ec6f2c4dbb51c89 patch_url = https://wrapdb.mesonbuild.com/v1/projects/fmt/5.3.0/1/get_zip patch_filename = fmt-5.3.0-1-wrap.zip patch_hash = 18f21a3b8833949c35d4ac88a7059577d5fa24b98786e4b1b2d3d81bb811440fwaybar-0.9.0/subprojects/gtk-layer-shell.wrap000066400000000000000000000004151360163675500212630ustar00rootroot00000000000000[wrap-file] directory = gtk-layer-shell-0.1.0 source_filename = gtk-layer-shell-0.1.0.tar.gz source_hash = f7569e27ae30b1a94c3ad6c955cf56240d6bc272b760d9d266ce2ccdb94a5cf0 source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.1.0/gtk-layer-shell-0.1.0.tar.gz waybar-0.9.0/subprojects/spdlog.wrap000066400000000000000000000006361360163675500175540ustar00rootroot00000000000000[wrap-file] directory = spdlog-1.3.1 source_url = https://github.com/gabime/spdlog/archive/v1.3.1.tar.gz source_filename = v1.3.1.tar.gz source_hash = 160845266e94db1d4922ef755637f6901266731c4cb3b30b45bf41efa0e6ab70 patch_url = https://wrapdb.mesonbuild.com/v1/projects/spdlog/1.3.1/1/get_zip patch_filename = spdlog-1.3.1-1-wrap.zip patch_hash = 715a0229781019b853d409cc0bf891ee4b9d3a17bec0cf87f4ad30b28bbecc87