From 6e7f22ac3ada6e27facee84fcbe2b3973eadbe1d Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 27 May 2020 09:10:38 +0200 Subject: [PATCH 001/355] fix: cancel thread and fix window close --- include/AModule.hpp | 6 +++-- include/bar.hpp | 2 +- include/modules/network.hpp | 2 -- include/util/rfkill.hpp | 4 +-- include/util/sleeper_thread.hpp | 5 ++++ src/AModule.cpp | 5 ++-- src/bar.cpp | 2 +- src/client.cpp | 3 ++- src/modules/mpd.cpp | 30 ++++++++++----------- src/modules/network.cpp | 47 --------------------------------- src/util/rfkill.cpp | 35 +++++++++++------------- 11 files changed, 47 insertions(+), 94 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index f7cc484..c9f1ae2 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -4,14 +4,15 @@ #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); + 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 &(); @@ -24,6 +25,7 @@ class AModule : public IModule { SCROLL_DIR getScrollDir(GdkEventScroll *e); bool tooltipEnabled(); + const std::string name_; const Json::Value &config_; Gtk::EventBox event_box_; diff --git a/include/bar.hpp b/include/bar.hpp index fb0cd59..63f0e22 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -34,10 +34,10 @@ class Bar { struct waybar_output *output; Json::Value config; - Gtk::Window window; struct wl_surface * surface; bool visible = true; bool vertical = false; + Gtk::Window window; private: static constexpr const char *MIN_HEIGHT_MSG = diff --git a/include/modules/network.hpp b/include/modules/network.hpp index edb5aa6..a0156fb 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -52,8 +52,6 @@ class Network : public ALabel { 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_; diff --git a/include/util/rfkill.hpp b/include/util/rfkill.hpp index 5dbf3ce..ac3d406 100644 --- a/include/util/rfkill.hpp +++ b/include/util/rfkill.hpp @@ -5,7 +5,7 @@ namespace waybar::util { class Rfkill { - public:; + public: Rfkill(enum rfkill_type rfkill_type); ~Rfkill() = default; void waitForEvent(); @@ -13,7 +13,7 @@ class Rfkill { private: enum rfkill_type rfkill_type_; - int state_ = 0; + int state_ = 0; }; } // namespace waybar::util diff --git a/include/util/sleeper_thread.hpp b/include/util/sleeper_thread.hpp index 642d47d..9adbe8f 100644 --- a/include/util/sleeper_thread.hpp +++ b/include/util/sleeper_thread.hpp @@ -59,6 +59,11 @@ class SleeperThread { do_run_ = false; } condvar_.notify_all(); + auto handle = thread_.native_handle(); + if (handle != 0) { + // TODO: find a proper way to terminate thread... + pthread_cancel(handle); + } } ~SleeperThread() { diff --git a/src/AModule.cpp b/src/AModule.cpp index 3066bfc..10bd077 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -6,7 +6,7 @@ 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)) { + : name_(std::move(name)), 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() || @@ -23,11 +23,12 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: AModule::~AModule() { for (const auto& pid : pid_) { if (pid != -1) { - kill(-pid, 9); + killpg(pid, SIGTERM); } } } + auto AModule::update() -> void { // Run user-provided update handler if configured if (config_["on-update"].isString()) { diff --git a/src/bar.cpp b/src/bar.cpp index 431a564..b2e84da 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -10,8 +10,8 @@ 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), + window{Gtk::WindowType::WINDOW_TOPLEVEL}, layer_surface_(nullptr), anchor_(ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP), left_(Gtk::ORIENTATION_HORIZONTAL, 0), diff --git a/src/client.cpp b/src/client.cpp index dee28a4..316e7ec 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -145,7 +145,8 @@ void waybar::Client::handleMonitorRemoved(Glib::RefPtr monitor) { for (auto it = bars.begin(); it != bars.end();) { if ((*it)->output->monitor == monitor) { auto output_name = (*it)->output->name; - (*it)->window.close(); + (*it)->window.hide(); + gtk_app->remove_window((*it)->window); it = bars.erase(it); spdlog::info("Bar removed from output: {}", output_name); } else { diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index af83123..19009cd 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -63,22 +63,20 @@ auto waybar::modules::MPD::update() -> void { 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) { - if (strcmp(e.what(), "Connection to MPD closed") == 0) { - spdlog::debug("{}: {}", module_name_, e.what()); - } else { - spdlog::warn("{}: {}", module_name_, e.what()); - } + 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) { + if (strcmp(e.what(), "Connection to MPD closed") == 0) { + spdlog::debug("{}: {}", module_name_, e.what()); + } else { + spdlog::warn("{}: {}", module_name_, e.what()); } } }); diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 0639d3e..a288c6a 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -81,8 +81,6 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf : 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), @@ -117,14 +115,6 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf } 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) { @@ -156,30 +146,6 @@ void waybar::modules::Network::createEventSocket() { } 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() { @@ -218,19 +184,6 @@ void waybar::modules::Network::worker() { } } }; - 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 { diff --git a/src/util/rfkill.cpp b/src/util/rfkill.cpp index df77598..f987f4c 100644 --- a/src/util/rfkill.cpp +++ b/src/util/rfkill.cpp @@ -3,11 +3,11 @@ * Copyright 2009 Johannes Berg * Copyright 2009 Marcel Holtmann * Copyright 2009 Tim Gardner - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -17,24 +17,24 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "util/rfkill.hpp" -#include -#include -#include -#include + #include +#include +#include #include +#include + #include +#include #include -waybar::util::Rfkill::Rfkill(const enum rfkill_type rfkill_type) - : rfkill_type_(rfkill_type) { -} +waybar::util::Rfkill::Rfkill(const enum rfkill_type rfkill_type) : rfkill_type_(rfkill_type) {} void waybar::util::Rfkill::waitForEvent() { struct rfkill_event event; - struct pollfd p; - ssize_t len; - int fd, n; + struct pollfd p; + ssize_t len; + int fd, n; fd = open("/dev/rfkill", O_RDONLY); if (fd < 0) { @@ -53,8 +53,7 @@ void waybar::util::Rfkill::waitForEvent() { break; } - if (n == 0) - continue; + if (n == 0) continue; len = read(fd, &event, sizeof(event)); if (len < 0) { @@ -67,17 +66,13 @@ void waybar::util::Rfkill::waitForEvent() { continue; } - if(event.type == rfkill_type_ && event.op == RFKILL_OP_CHANGE) { + if (event.type == rfkill_type_ && event.op == RFKILL_OP_CHANGE) { state_ = event.soft || event.hard; break; } } close(fd); - return; } - -bool waybar::util::Rfkill::getState() const { - return state_; -} +bool waybar::util::Rfkill::getState() const { return state_; } From e96a0bf7996cc78715ce08b3a23767b08fb1acf8 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 30 May 2020 11:59:22 +0200 Subject: [PATCH 002/355] refactor(custom): tooltip markup --- src/modules/custom.cpp | 4 ++-- src/modules/temperature.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 7ab7a06..5643160 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -129,9 +129,9 @@ auto waybar::modules::Custom::update() -> void { label_.set_markup(str); if (tooltipEnabled()) { if (text_ == tooltip_) { - label_.set_tooltip_text(str); + label_.set_tooltip_markup(str); } else { - label_.set_tooltip_text(tooltip_); + label_.set_tooltip_markup(tooltip_); } } auto classes = label_.get_style_context()->list_classes(); diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index bf0bd1c..dc6b2d7 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -6,7 +6,7 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val if (config_["hwmon-path"].isString()) { file_path_ = config_["hwmon-path"].asString(); } else if (config_["hwmon-path-abs"].isString() && config_["input-filename"].isString()) { - file_path_ = (*std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())).path().u8string() + "/" + config_["input-filename"].asString(); + file_path_ = (*std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())).path().string() + "/" + config_["input-filename"].asString(); } else { auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); From adaf84304865e143e4e83984aaea6f6a7c9d4d96 Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Sat, 30 May 2020 12:07:38 +0200 Subject: [PATCH 003/355] foreign-toplevel-manager based taskbar module (#692) Co-authored-by: Alex --- include/factory.hpp | 3 + include/modules/wlr/taskbar.hpp | 160 +++++ man/waybar-wlr-taskbar.5.scd | 104 +++ man/waybar.5.scd | 1 + meson.build | 6 + protocol/meson.build | 1 + ...oreign-toplevel-management-unstable-v1.xml | 259 +++++++ src/factory.cpp | 5 + src/modules/wlr/taskbar.cpp | 658 ++++++++++++++++++ 9 files changed, 1197 insertions(+) create mode 100644 include/modules/wlr/taskbar.hpp create mode 100644 man/waybar-wlr-taskbar.5.scd create mode 100644 protocol/wlr-foreign-toplevel-management-unstable-v1.xml create mode 100644 src/modules/wlr/taskbar.cpp diff --git a/include/factory.hpp b/include/factory.hpp index c698aa3..28273eb 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -7,6 +7,9 @@ #include "modules/sway/window.hpp" #include "modules/sway/workspaces.hpp" #endif +#ifdef HAVE_WLR +#include "modules/wlr/taskbar.hpp" +#endif #if defined(__linux__) && !defined(NO_FILESYSTEM) #include "modules/battery.hpp" #endif diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp new file mode 100644 index 0000000..5bcb7ec --- /dev/null +++ b/include/modules/wlr/taskbar.hpp @@ -0,0 +1,160 @@ +#pragma once + +#include "AModule.hpp" +#include "bar.hpp" +#include "client.hpp" +#include "util/json.hpp" + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" + + +namespace waybar::modules::wlr { + +class Taskbar; + +class Task +{ + public: + Task(const waybar::Bar&, const Json::Value&, Taskbar*, + struct zwlr_foreign_toplevel_handle_v1 *, struct wl_seat*); + ~Task(); + + public: + enum State { + MAXIMIZED = (1 << 0), + MINIMIZED = (1 << 1), + ACTIVE = (1 << 2), + FULLSCREEN = (1 << 3), + INVALID = (1 << 4) + }; + + private: + static uint32_t global_id; + + private: + const waybar::Bar &bar_; + const Json::Value &config_; + Taskbar *tbar_; + struct zwlr_foreign_toplevel_handle_v1 *handle_; + struct wl_seat *seat_; + + uint32_t id_; + + Gtk::Button button_; + Gtk::Box content_; + Gtk::Image icon_; + Gtk::Label text_before_; + Gtk::Label text_after_; + bool button_visible_; + + bool with_icon_; + std::string format_before_; + std::string format_after_; + + std::string format_tooltip_; + + std::string title_; + std::string app_id_; + uint32_t state_; + + private: + std::string repr() const; + std::string state_string(bool = false) const; + + public: + /* Getter functions */ + uint32_t id() const { return id_; } + std::string title() const { return title_; } + std::string app_id() const { return app_id_; } + uint32_t state() const { return state_; } + bool maximized() const { return state_ & MAXIMIZED; } + bool minimized() const { return state_ & MINIMIZED; } + bool active() const { return state_ & ACTIVE; } + bool fullscreen() const { return state_ & FULLSCREEN; } + + public: + /* Callbacks for the wlr protocol */ + void handle_title(const char *); + void handle_app_id(const char *); + void handle_output_enter(struct wl_output *); + void handle_output_leave(struct wl_output *); + void handle_state(struct wl_array *); + void handle_done(); + void handle_closed(); + + /* Callbacks for Gtk events */ + bool handle_clicked(GdkEventButton *); + + public: + bool operator==(const Task&) const; + bool operator!=(const Task&) const; + + public: + void update(); + + public: + /* Interaction with the tasks */ + void maximize(bool); + void minimize(bool); + void activate(); + void fullscreen(bool); + void close(); +}; + +using TaskPtr = std::unique_ptr; + + +class Taskbar : public waybar::AModule +{ + public: + Taskbar(const std::string&, const waybar::Bar&, const Json::Value&); + ~Taskbar(); + void update(); + + private: + const waybar::Bar &bar_; + Gtk::Box box_; + std::vector tasks_; + + Glib::RefPtr icon_theme_; + + struct zwlr_foreign_toplevel_manager_v1 *manager_; + struct wl_seat *seat_; + + public: + /* Callbacks for global registration */ + void register_manager(struct wl_registry*, uint32_t name, uint32_t version); + void register_seat(struct wl_registry*, uint32_t name, uint32_t version); + + /* Callbacks for the wlr protocol */ + void handle_toplevel_create(struct zwlr_foreign_toplevel_handle_v1 *); + void handle_finished(); + + public: + void add_button(Gtk::Button &); + void move_button(Gtk::Button &, int); + void remove_button(Gtk::Button &); + void remove_task(uint32_t); + + bool show_output(struct wl_output *) const; + bool all_outputs() const; + + Glib::RefPtr icon_theme() const; +}; + +} /* namespace waybar::modules::wlr */ diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd new file mode 100644 index 0000000..cbce6e7 --- /dev/null +++ b/man/waybar-wlr-taskbar.5.scd @@ -0,0 +1,104 @@ +waybar-wlr-taskbar(5) + +# NAME + +wlroots - Taskbar module + +# DESCRIPTION + +The *taskbar* module displays the currently open applications. This module requires +a compositor that implements the foreign-toplevel-manager interface. + +# CONFIGURATION + +Addressed by *wlr/taskbar* + +*all-outputs*: ++ + typeof: bool ++ + default: false ++ + If set to false applications on the waybar's current output will be shown. Otherwise all applications are shown. + +*format*: ++ + typeof: string ++ + default: {icon} ++ + The format, how information should be displayed. + +*icon-theme*: ++ + typeof: string ++ + The name of the icon-theme that should be used. If omitted, the system default will be used. + +*icon-size*: ++ + typeof: integer ++ + default: 16 ++ + The size of the icon. + +*tooltip*: ++ + typeof: bool ++ + default: true ++ + If set to false no tooltip will be shown. + +*tooltip-format*: ++ + typeof: string ++ + default: {title} ++ + The format, how information in the tooltip should be displayed. + +*active-first*: ++ + typeof: bool ++ + default: false ++ + If set to true, always reorder the tasks in the taskbar so that the currently active one is first. Otherwise don't reorder. + +*on-click*: ++ + typeof: string ++ + The action which should be triggered when clicking on the application button with the left mouse button. + +*on-click-middle*: ++ + typeof: string ++ + The action which should be triggered when clicking on the application button with the middle mouse button. + +*on-click-right*: ++ + typeof: string ++ + The action which should be triggered when clicking on the application button with the right mouse button. + +*on-update*: ++ + typeof: string ++ + Command to execute when the module is updated. + +# FORMAT REPLACEMENTS + +*{icon}*: The icon of the application. + +*{title}*: The title of the application. + +*{app_id}*: The app_id (== application name) of the application. + +*{state}*: The state (minimized, maximized, active, fullscreen) of the application. + +*{short_state}*: The state (minimize == m, maximized == M, active == A, fullscreen == F) represented as one character of the application. + +# CLICK ACTIONS + +*activate*: Bring the application into foreground. +*minimize*: Minimize the application. +*maximize*: Maximize the application. +*fullscreen*: Set the application to fullscreen. +*close*: Close the application. + +# EXAMPLES + +``` +"wlr/taskbar": { + "format": "{icon}", + "tooltip-format": "{title}", + "on-click": "activate", + "on-middle-click": "close" +} +``` + +# Style + +- *#taskbar* +- *#taskbar button* +- *#taskbar button.maximized* +- *#taskbar button.minimized* +- *#taskbar button.active* +- *#taskbar button.fullscreen* diff --git a/man/waybar.5.scd b/man/waybar.5.scd index 1e8004f..9ff1891 100644 --- a/man/waybar.5.scd +++ b/man/waybar.5.scd @@ -196,5 +196,6 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270. - *waybar-sway-mode(5)* - *waybar-sway-window(5)* - *waybar-sway-workspaces(5)* +- *waybar-wlr-taskbar(5)* - *waybar-temperature(5)* - *waybar-tray(5)* diff --git a/meson.build b/meson.build index c16d785..496fc3a 100644 --- a/meson.build +++ b/meson.build @@ -165,6 +165,11 @@ if true # find_program('sway', required : false).found() ] endif +if true + add_project_arguments('-DHAVE_WLR', language: 'cpp') + src_files += 'src/modules/wlr/taskbar.cpp' +endif + if libnl.found() and libnlgen.found() add_project_arguments('-DHAVE_LIBNL', language: 'cpp') src_files += 'src/modules/network.cpp' @@ -260,6 +265,7 @@ if scdoc.found() 'waybar-temperature.5.scd', 'waybar-tray.5.scd', 'waybar-states.5.scd', + 'waybar-wlr-taskbar.5.scd', 'waybar-bluetooth.5.scd', ] diff --git a/protocol/meson.build b/protocol/meson.build index 0699a9d..f4146ae 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -26,6 +26,7 @@ client_protocols = [ [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'], + ['wlr-foreign-toplevel-management-unstable-v1.xml'], ] client_protos_src = [] diff --git a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml new file mode 100644 index 0000000..a97738f --- /dev/null +++ b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml @@ -0,0 +1,259 @@ + + + + Copyright © 2018 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + The purpose of this protocol is to enable the creation of taskbars + and docks by providing them with a list of opened applications and + letting them request certain actions on them, like maximizing, etc. + + After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + toplevel window will be sent via the toplevel event + + + + + This event is emitted whenever a new toplevel window is created. It + is emitted for all toplevels, regardless of the app that has created + them. + + All initial details of the toplevel(title, app_id, states, etc.) will + be sent immediately after this event via the corresponding events in + zwlr_foreign_toplevel_handle_v1. + + + + + + + Indicates the client no longer wishes to receive events for new toplevels. + However the compositor may emit further toplevel_created events, until + the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + This event indicates that the compositor is done sending events to the + zwlr_foreign_toplevel_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + window. Each app may have multiple opened toplevels. + + Each toplevel has a list of outputs it is visible on, conveyed to the + client with the output_enter and output_leave events. + + + + + This event is emitted whenever the title of the toplevel changes. + + + + + + + This event is emitted whenever the app-id of the toplevel changes. + + + + + + + This event is emitted whenever the toplevel becomes visible on + the given output. A toplevel may be visible on multiple outputs. + + + + + + + This event is emitted whenever the toplevel stops being visible on + the given output. It is guaranteed that an entered-output event + with the same output has been emitted before this event. + + + + + + + Requests that the toplevel be maximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unmaximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be minimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unminimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Request that this toplevel be activated on the given seat. + There is no guarantee the toplevel will be actually activated. + + + + + + + The different states that a toplevel can have. These have the same meaning + as the states with the same names defined in xdg-toplevel + + + + + + + + + + + This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 + is created and each time the toplevel state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + + This event is sent after all changes in the toplevel state have been + sent. + + This allows changes to the zwlr_foreign_toplevel_handle_v1 properties + to be seen as atomic, even if they happen via multiple events. + + + + + + Send a request to the toplevel to close itself. The compositor would + typically use a shell-specific method to carry out this request, for + example by sending the xdg_toplevel.close event. However, this gives + no guarantees the toplevel will actually be destroyed. If and when + this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + be emitted. + + + + + + The rectangle of the surface specified in this request corresponds to + the place where the app using this protocol represents the given toplevel. + It can be used by the compositor as a hint for some operations, e.g + minimizing. The client is however not required to set this, in which + case the compositor is free to decide some default value. + + If the client specifies more than one rectangle, only the last one is + considered. + + The dimensions are given in surface-local coordinates. + Setting width=height=0 removes the already-set rectangle. + + + + + + + + + + + + + + + + This event means the toplevel has been destroyed. It is guaranteed there + won't be any more events for this zwlr_foreign_toplevel_handle_v1. The + toplevel itself becomes inert so any requests will be ignored except the + destroy request. + + + + + + Destroys the zwlr_foreign_toplevel_handle_v1 object. + + This request should be called either when the client does not want to + use the toplevel anymore or after the closed event to finalize the + destruction of the object. + + + + + + + + Requests that the toplevel be fullscreened on the given output. If the + fullscreen state and/or the outputs the toplevel is visible on actually + change, this will be indicated by the state and output_enter/leave + events. + + The output parameter is only a hint to the compositor. Also, if output + is NULL, the compositor should decide which output the toplevel will be + fullscreened on, if at all. + + + + + + + Requests that the toplevel be unfullscreened. If the fullscreen state + actually changes, this will be indicated by the state event. + + + + diff --git a/src/factory.cpp b/src/factory.cpp index 6005cad..f09ea31 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -22,6 +22,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "sway/window") { return new waybar::modules::sway::Window(id, bar_, config_[name]); } +#endif +#ifdef HAVE_WLR + if (ref == "wlr/taskbar") { + return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]); + } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp new file mode 100644 index 0000000..845f07c --- /dev/null +++ b/src/modules/wlr/taskbar.cpp @@ -0,0 +1,658 @@ +#include "modules/wlr/taskbar.hpp" + +#include "glibmm/refptr.h" +#include "util/format.hpp" + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + + +namespace waybar::modules::wlr { + +/* Icon loading functions */ + +/* Method 1 - get the correct icon name from the desktop file */ +static std::string get_from_desktop_app_info(const std::string &app_id) +{ + Glib::RefPtr app_info; + + std::vector prefixes = { + "", + "/usr/share/applications/", + "/usr/share/applications/kde/", + "/usr/share/applications/org.kde.", + "/usr/local/share/applications/", + "/usr/local/share/applications/org.kde.", + }; + + std::string lower_app_id = app_id; + std::transform(std::begin(lower_app_id), std::end(lower_app_id), std::begin(lower_app_id), + [](unsigned char c) { return std::tolower(c); }); + + + std::vector app_id_variations = { + app_id, + lower_app_id + }; + + std::vector suffixes = { + "", + ".desktop" + }; + + for (auto& prefix : prefixes) + for (auto& id : app_id_variations) + for (auto& suffix : suffixes) + if (!app_info) + app_info = Gio::DesktopAppInfo::create_from_filename(prefix + id + suffix); + + if (app_info) + return app_info->get_icon()->to_string(); + + return ""; +} + +/* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */ +static std::string get_from_icon_theme(Glib::RefPtr icon_theme, + const std::string &app_id) { + + if (icon_theme->lookup_icon(app_id, 24)) + return app_id; + + return ""; +} + +static bool image_load_icon(Gtk::Image& image, Glib::RefPtr icon_theme, + const std::string &app_id_list, int size) +{ + std::string app_id; + std::istringstream stream(app_id_list); + bool found = false; + + + /* Wayfire sends a list of app-id's in space separated format, other compositors + * send a single app-id, but in any case this works fine */ + while (stream >> app_id) + { + std::string icon_name = get_from_desktop_app_info(app_id); + if (icon_name.empty()) + icon_name = get_from_icon_theme(icon_theme, app_id); + + if (icon_name.empty()) + continue; + + auto pixbuf = icon_theme->load_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE); + if (pixbuf) { + image.set(pixbuf); + found = true; + break; + } + } + + return found; +} + +/* Task class implementation */ +uint32_t Task::global_id = 0; + +static void tl_handle_title(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + const char *title) +{ + return static_cast(data)->handle_title(title); +} + +static void tl_handle_app_id(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + const char *app_id) +{ + return static_cast(data)->handle_app_id(app_id); +} + +static void tl_handle_output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + struct wl_output *output) +{ + return static_cast(data)->handle_output_enter(output); +} + +static void tl_handle_output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + struct wl_output *output) +{ + return static_cast(data)->handle_output_leave(output); +} + +static void tl_handle_state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + struct wl_array *state) +{ + return static_cast(data)->handle_state(state); +} + +static void tl_handle_done(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) +{ + return static_cast(data)->handle_done(); +} + +static void tl_handle_closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) +{ + return static_cast(data)->handle_closed(); +} + +static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_impl = { + .title = tl_handle_title, + .app_id = tl_handle_app_id, + .output_enter = tl_handle_output_enter, + .output_leave = tl_handle_output_leave, + .state = tl_handle_state, + .done = tl_handle_done, + .closed = tl_handle_closed, +}; + +Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, + struct zwlr_foreign_toplevel_handle_v1 *tl_handle, struct wl_seat *seat) : + bar_{bar}, config_{config}, tbar_{tbar}, handle_{tl_handle}, seat_{seat}, + id_{global_id++}, + content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, + button_visible_{false} +{ + zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this); + + button_.set_relief(Gtk::RELIEF_NONE); + + content_.add(text_before_); + content_.add(icon_); + content_.add(text_after_); + + content_.show(); + button_.add(content_); + + with_icon_ = false; + format_before_.clear(); + format_after_.clear(); + + if (config_["format"].isString()) { + /* The user defined a format string, use it */ + auto format = config_["format"].asString(); + + auto icon_pos = format.find("{icon}"); + if (icon_pos == 0) { + with_icon_ = true; + format_after_ = format.substr(6); + } else if (icon_pos == std::string::npos) { + format_after_ = format; + } else { + with_icon_ = true; + format_before_ = format.substr(0, icon_pos); + format_after_ = format.substr(icon_pos + 6); + } + } else { + /* The default is to only show the icon */ + with_icon_ = true; + } + + /* Strip spaces at the beginning and end of the format strings */ + if (!format_before_.empty() && format_before_.back() == ' ') + format_before_.pop_back(); + if (!format_after_.empty() && format_after_.front() == ' ') + format_after_.erase(std::cbegin(format_after_)); + + format_tooltip_.clear(); + if (!config_["tooltip"].isBool() || config_["tooltip"].asBool()) { + if (config_["tooltip-format"].isString()) + format_tooltip_ = config_["tooltip-format"].asString(); + else + format_tooltip_ = "{title}"; + } + + /* Handle click events if configured */ + if (config_["on-click"].isString() || config_["on-click-middle"].isString() + || config_["on-click-left"].isString()) { + button_.add_events(Gdk::BUTTON_PRESS_MASK); + button_.signal_button_press_event().connect( + sigc::mem_fun(*this, &Task::handle_clicked), false); + } +} + +Task::~Task() +{ + if (handle_) { + zwlr_foreign_toplevel_handle_v1_destroy(handle_); + handle_ = nullptr; + } + if (button_visible_) { + tbar_->remove_button(button_); + button_visible_ = false; + } +} + +std::string Task::repr() const +{ + std::stringstream ss; + ss << "Task (" << id_ << ") " << title_ << " [" << app_id_ << "] <" + << (active() ? "A" : "a") + << (maximized() ? "M" : "m") + << (minimized() ? "I" : "i") + << (fullscreen() ? "F" : "f") + << ">"; + + return ss.str(); +} + +std::string Task::state_string(bool shortened) const +{ + std::stringstream ss; + if (shortened) + ss << (minimized() ? "m" : "") << (maximized() ? "M" : "") + << (active() ? "A" : "") << (fullscreen() ? "F" : ""); + else + ss << (minimized() ? "minimized " : "") << (maximized() ? "maximized " : "") + << (active() ? "active " : "") << (fullscreen() ? "fullscreen " : ""); + + std::string res = ss.str(); + if (shortened || res.empty()) + return res; + else + return res.substr(0, res.size() - 1); +} + +void Task::handle_title(const char *title) +{ + title_ = title; +} + +void Task::handle_app_id(const char *app_id) +{ + app_id_ = app_id; + if (!image_load_icon(icon_, tbar_->icon_theme(), app_id_, + config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16)) + spdlog::warn("Failed to load icon for {}", app_id); + + if (with_icon_) + icon_.show(); +} + +void Task::handle_output_enter(struct wl_output *output) +{ + spdlog::debug("{} entered output {}", repr(), (void*)output); + + if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) { + /* The task entered the output of the current bar make the button visible */ + tbar_->add_button(button_); + button_.show(); + button_visible_ = true; + spdlog::debug("{} now visible on {}", repr(), bar_.output->name); + } +} + +void Task::handle_output_leave(struct wl_output *output) +{ + spdlog::debug("{} left output {}", repr(), (void*)output); + + if (button_visible_ && !tbar_->all_outputs() && tbar_->show_output(output)) { + /* The task left the output of the current bar, make the button invisible */ + tbar_->remove_button(button_); + button_.hide(); + button_visible_ = false; + spdlog::debug("{} now invisible on {}", repr(), bar_.output->name); + } +} + +void Task::handle_state(struct wl_array *state) +{ + state_ = 0; + for (uint32_t* entry = static_cast(state->data); + entry < static_cast(state->data) + state->size; + entry++) { + if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) + state_ |= MAXIMIZED; + if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) + state_ |= MINIMIZED; + if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) + state_ |= ACTIVE; + if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) + state_ |= FULLSCREEN; + } +} + +void Task::handle_done() +{ + spdlog::debug("{} changed", repr()); + + if (state_ & MAXIMIZED) { + button_.get_style_context()->add_class("maximized"); + } else if (!(state_ & MAXIMIZED)) { + button_.get_style_context()->remove_class("maximized"); + } + + if (state_ & MINIMIZED) { + button_.get_style_context()->add_class("minimized"); + } else if (!(state_ & MINIMIZED)) { + button_.get_style_context()->remove_class("minimized"); + } + + if (state_ & ACTIVE) { + button_.get_style_context()->add_class("active"); + } else if (!(state_ & ACTIVE)) { + button_.get_style_context()->remove_class("active"); + } + + if (state_ & FULLSCREEN) { + button_.get_style_context()->add_class("fullscreen"); + } else if (!(state_ & FULLSCREEN)) { + button_.get_style_context()->remove_class("fullscreen"); + } + + if (config_["active-first"].isBool() && config_["active-first"].asBool() && active()) + tbar_->move_button(button_, 0); + + tbar_->dp.emit(); +} + +void Task::handle_closed() +{ + spdlog::debug("{} closed", repr()); + zwlr_foreign_toplevel_handle_v1_destroy(handle_); + handle_ = nullptr; + if (button_visible_) { + tbar_->remove_button(button_); + button_visible_ = false; + } + tbar_->remove_task(id_); +} + +bool Task::handle_clicked(GdkEventButton *bt) +{ + std::string action; + if (config_["on-click"].isString() && bt->button == 1) + action = config_["on-click"].asString(); + else if (config_["on-click-middle"].isString() && bt->button == 2) + action = config_["on-click-middle"].asString(); + else if (config_["on-click-right"].isString() && bt->button == 3) + action = config_["on-click-right"].asString(); + + if (action.empty()) + return true; + else if (action == "activate") + activate(); + else if (action == "minimize") + minimize(!minimized()); + else if (action == "maximize") + maximize(!maximized()); + else if (action == "fullscreen") + fullscreen(!fullscreen()); + else if (action == "close") + close(); + else + spdlog::warn("Unknown action {}", action); + + return true; +} + +bool Task::operator==(const Task &o) const +{ + return o.id_ == id_; +} + +bool Task::operator!=(const Task &o) const +{ + return o.id_ != id_; +} + +void Task::update() +{ + if (!format_before_.empty()) { + text_before_.set_label( + fmt::format(format_before_, + fmt::arg("title", title_), + fmt::arg("app_id", app_id_), + fmt::arg("state", state_string()), + fmt::arg("short_state", state_string(true)) + ) + ); + text_before_.show(); + } + if (!format_after_.empty()) { + text_after_.set_label( + fmt::format(format_before_, + fmt::arg("title", title_), + fmt::arg("app_id", app_id_), + fmt::arg("state", state_string()), + fmt::arg("short_state", state_string(true)) + ) + ); + text_after_.show(); + } + + if (!format_tooltip_.empty()) { + button_.set_tooltip_markup( + fmt::format(format_tooltip_, + fmt::arg("title", title_), + fmt::arg("app_id", app_id_), + fmt::arg("state", state_string()), + fmt::arg("short_state", state_string(true)) + ) + ); + } +} + +void Task::maximize(bool set) +{ + if (set) + zwlr_foreign_toplevel_handle_v1_set_maximized(handle_); + else + zwlr_foreign_toplevel_handle_v1_unset_maximized(handle_); +} + +void Task::minimize(bool set) +{ + if (set) + zwlr_foreign_toplevel_handle_v1_set_minimized(handle_); + else + zwlr_foreign_toplevel_handle_v1_unset_minimized(handle_); +} + +void Task::activate() +{ + zwlr_foreign_toplevel_handle_v1_activate(handle_, seat_); +} + +void Task::fullscreen(bool set) +{ + if (set) + zwlr_foreign_toplevel_handle_v1_set_fullscreen(handle_, nullptr); + else + zwlr_foreign_toplevel_handle_v1_unset_fullscreen(handle_); +} + +void Task::close() +{ + zwlr_foreign_toplevel_handle_v1_close(handle_); +} + + +/* Taskbar class implementation */ +static void handle_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) +{ + if (std::strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) { + static_cast(data)->register_manager(registry, name, version); + } else if (std::strcmp(interface, wl_seat_interface.name) == 0) { + static_cast(data)->register_seat(registry, name, version); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ + /* Nothing to do here */ +} + +static const wl_registry_listener registry_listener_impl = { + .global = handle_global, + .global_remove = handle_global_remove +}; + +Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Value &config) + : waybar::AModule(config, "taskbar", id, false, false), + bar_(bar), + box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, + manager_{nullptr}, seat_{nullptr} +{ + box_.set_name("taskbar"); + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + event_box_.add(box_); + + struct wl_display *display = Client::inst()->wl_display; + struct wl_registry *registry = wl_display_get_registry(display); + + wl_registry_add_listener(registry, ®istry_listener_impl, this); + wl_display_roundtrip(display); + + if (!manager_) { + spdlog::error("Failed to register as toplevel manager"); + return; + } + if (!seat_) { + spdlog::error("Failed to get wayland seat"); + return; + } + + /* Get the configured icon theme if specified */ + if (config_["icon-theme"].isString()) { + icon_theme_ = Gtk::IconTheme::create(); + icon_theme_->set_custom_theme(config_["icon-theme"].asString()); + spdlog::debug("Use custom icon theme: {}.", config_["icon-theme"].asString()); + } else { + spdlog::debug("Use system default icon theme"); + icon_theme_ = Gtk::IconTheme::get_default(); + } +} + +Taskbar::~Taskbar() +{ + if (manager_) { + zwlr_foreign_toplevel_manager_v1_destroy(manager_); + manager_ = nullptr; + } +} + +void Taskbar::update() +{ + for (auto& t : tasks_) { + t->update(); + } + + AModule::update(); +} + +static void tm_handle_toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager, + struct zwlr_foreign_toplevel_handle_v1 *tl_handle) +{ + return static_cast(data)->handle_toplevel_create(tl_handle); +} + +static void tm_handle_finished(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager) +{ + return static_cast(data)->handle_finished(); +} + +static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_impl = { + .toplevel = tm_handle_toplevel, + .finished = tm_handle_finished, +}; + +void Taskbar::register_manager(struct wl_registry *registry, uint32_t name, uint32_t version) +{ + if (manager_) { + spdlog::warn("Register foreign toplevel manager again although already existing!"); + return; + } + if (version != 2) { + spdlog::warn("Using different foreign toplevel manager protocol version: {}", version); + } + + manager_ = static_cast(wl_registry_bind(registry, name, + &zwlr_foreign_toplevel_manager_v1_interface, version)); + + if (manager_) + zwlr_foreign_toplevel_manager_v1_add_listener(manager_, &toplevel_manager_impl, this); + else + spdlog::debug("Failed to register manager"); +} + +void Taskbar::register_seat(struct wl_registry *registry, uint32_t name, uint32_t version) +{ + if (seat_) { + spdlog::warn("Register seat again although already existing!"); + return; + } + + seat_ = static_cast(wl_registry_bind(registry, name, &wl_seat_interface, version)); +} + +void Taskbar::handle_toplevel_create(struct zwlr_foreign_toplevel_handle_v1 *tl_handle) +{ + tasks_.push_back(std::make_unique(bar_, config_, this, tl_handle, seat_)); +} + +void Taskbar::handle_finished() +{ + zwlr_foreign_toplevel_manager_v1_destroy(manager_); + manager_ = nullptr; +} + +void Taskbar::add_button(Gtk::Button &bt) +{ + box_.pack_start(bt, false, false); +} + +void Taskbar::move_button(Gtk::Button &bt, int pos) +{ + box_.reorder_child(bt, pos); +} + +void Taskbar::remove_button(Gtk::Button &bt) +{ + box_.remove(bt); +} + +void Taskbar::remove_task(uint32_t id) +{ + auto it = std::find_if(std::begin(tasks_), std::end(tasks_), + [id](const TaskPtr &p) { return p->id() == id; }); + + if (it == std::end(tasks_)) { + spdlog::warn("Can't find task with id {}", id); + return; + } + + tasks_.erase(it); +} + +bool Taskbar::show_output(struct wl_output *output) const +{ + return output == gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); +} + +bool Taskbar::all_outputs() const +{ + static bool result = config_["all_outputs"].isBool() ? config_["all_outputs"].asBool() : false; + + return result; +} + +Glib::RefPtr Taskbar::icon_theme() const +{ + return icon_theme_; +} + +} /* namespace waybar::modules::wlr */ From 4b2e6b54a7fcfa5aae2bbc06f8683bbcf3855849 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 1 Jun 2020 13:29:41 +0200 Subject: [PATCH 004/355] Update FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a8c110b..3a0ca11 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,4 @@ # These are supported funding model platforms +github: Alexays custom: https://paypal.me/ARouillard From a50c12b6aefb3650afabc43575f9408d766e7425 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Mon, 8 Jun 2020 11:01:06 +0800 Subject: [PATCH 005/355] Fix typos --- man/waybar-battery.5.scd | 2 +- man/waybar-network.5.scd | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 75d4108..917a03d 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -20,7 +20,7 @@ The *battery* module displays the current capacity and state (eg. charging) of y *full-at*: ++ typeof: integer ++ - Define the max percentage of the battery, usefull for an old battery, e.g. 96 + Define the max percentage of the battery, useful for an old battery, e.g. 96 *interval*: ++ typeof: integer ++ diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index aa6eb3f..ab459ae 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -33,7 +33,7 @@ Addressed by *network* *format-ethernet*: ++ typeof: string ++ - This format is used when a ethernet interface is displayed. + This format is used when an ethernet interface is displayed. *format-wifi*: ++ typeof: string ++ @@ -103,7 +103,7 @@ Addressed by *network* *tooltip-format-ethernet*: ++ typeof: string ++ - This format is used when a ethernet interface is displayed. + This format is used when an ethernet interface is displayed. *tooltip-format-wifi*: ++ typeof: string ++ From 0080feb9afaa0fa7d8e0009f570da62560eddb8b Mon Sep 17 00:00:00 2001 From: Tudor Brindus Date: Sun, 7 Jun 2020 14:51:43 -0400 Subject: [PATCH 006/355] sway/workspaces: make clicking on workspaces idempotent Previously, clicking on the same workspace you were on would throw you to another workspace if `workspace_auto_back_and_forth yes` was specified in your sway config. This also fixes workspace output moving misbehaving and doing the same. --- include/modules/sway/workspaces.hpp | 2 ++ src/modules/sway/workspaces.cpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 5454b9e..228c71c 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -20,6 +20,8 @@ class Workspaces : public AModule, public sigc::trackable { auto update() -> void; private: + static inline const std::string workspace_switch_cmd_ = "workspace --no-auto-back-and-forth \"{}\""; + void onCmd(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&); bool filterButtons(); diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 60d9887..5f7c3f6 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -219,12 +219,12 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { if (node["target_output"].isString()) { ipc_.sendCmd( IPC_COMMAND, - fmt::format("workspace \"{}\"; move workspace to output \"{}\"; workspace \"{}\"", + fmt::format(workspace_switch_cmd_ + "; move workspace to output \"{}\"; " + workspace_switch_cmd_, node["name"].asString(), node["target_output"].asString(), node["name"].asString())); } else { - ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", node["name"].asString())); + ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, node["name"].asString())); } } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); @@ -276,7 +276,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { } } try { - ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace \"{}\"", name)); + ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, name)); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } From 343a8bef225865c3f0ef04d3536a5ddb1840c927 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 6 Jun 2020 15:41:37 +0200 Subject: [PATCH 007/355] river/tags: add module --- include/factory.hpp | 3 + include/modules/river/tags.hpp | 31 +++++++ man/waybar-river-tags.5.scd | 38 +++++++++ meson.build | 6 ++ protocol/meson.build | 1 + protocol/river-status-unstable-v1.xml | 116 +++++++++++++++++++++++++ src/factory.cpp | 7 +- src/modules/river/tags.cpp | 118 ++++++++++++++++++++++++++ 8 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 include/modules/river/tags.hpp create mode 100644 man/waybar-river-tags.5.scd create mode 100644 protocol/river-status-unstable-v1.xml create mode 100644 src/modules/river/tags.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 28273eb..fcbf3a2 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -10,6 +10,9 @@ #ifdef HAVE_WLR #include "modules/wlr/taskbar.hpp" #endif +#ifdef HAVE_RIVER +#include "modules/river/tags.hpp" +#endif #if defined(__linux__) && !defined(NO_FILESYSTEM) #include "modules/battery.hpp" #endif diff --git a/include/modules/river/tags.hpp b/include/modules/river/tags.hpp new file mode 100644 index 0000000..f80b3c5 --- /dev/null +++ b/include/modules/river/tags.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "river-status-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" + +namespace waybar::modules::river { + +class Tags : public waybar::AModule { + public: + Tags(const std::string &, const waybar::Bar &, const Json::Value &); + ~Tags(); + + // Handlers for wayland events + void handle_focused_tags(uint32_t tags); + void handle_view_tags(struct wl_array *tags); + + struct zriver_status_manager_v1 *status_manager_; + + private: + const waybar::Bar & bar_; + Gtk::Box box_; + std::vector buttons_; + struct zriver_output_status_v1 *output_status_; +}; + +} /* namespace waybar::modules::river */ diff --git a/man/waybar-river-tags.5.scd b/man/waybar-river-tags.5.scd new file mode 100644 index 0000000..a02ddeb --- /dev/null +++ b/man/waybar-river-tags.5.scd @@ -0,0 +1,38 @@ +waybar-river-tags(5) + +# NAME + +waybar - river tags module + +# DESCRIPTION + +The *tags* module displays the current state of tags in river. + +# CONFIGURATION + +Addressed by *river/tags* + +*num-tags*: ++ + typeof: uint ++ + default: 9 ++ + The number of tags that should be displayed. + +# EXAMPLE + +``` +"river/tags": { + "num-tags": 5 +} +``` + +# STYLE + +- *#tags button* +- *#tags button.occupied* +- *#tags button.focused* + +Note that a tag can be both occupied and focused at the same time. + +# SEE ALSO + +waybar(5), river(1) diff --git a/meson.build b/meson.build index 496fc3a..8d6dc16 100644 --- a/meson.build +++ b/meson.build @@ -170,6 +170,11 @@ if true src_files += 'src/modules/wlr/taskbar.cpp' endif +if true + add_project_arguments('-DHAVE_RIVER', language: 'cpp') + src_files += 'src/modules/river/tags.cpp' +endif + if libnl.found() and libnlgen.found() add_project_arguments('-DHAVE_LIBNL', language: 'cpp') src_files += 'src/modules/network.cpp' @@ -259,6 +264,7 @@ if scdoc.found() 'waybar-mpd.5.scd', 'waybar-network.5.scd', 'waybar-pulseaudio.5.scd', + 'waybar-river-tags.5.scd', 'waybar-sway-mode.5.scd', 'waybar-sway-window.5.scd', 'waybar-sway-workspaces.5.scd', diff --git a/protocol/meson.build b/protocol/meson.build index f4146ae..07d524a 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -27,6 +27,7 @@ client_protocols = [ [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['wlr-foreign-toplevel-management-unstable-v1.xml'], + ['river-status-unstable-v1.xml'], ] client_protos_src = [] diff --git a/protocol/river-status-unstable-v1.xml b/protocol/river-status-unstable-v1.xml new file mode 100644 index 0000000..a4d6f4e --- /dev/null +++ b/protocol/river-status-unstable-v1.xml @@ -0,0 +1,116 @@ + + + + Copyright 2020 Isaac Freund + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + + + A global factory for objects that receive status information specific + to river. It could be used to implement, for example, a status bar. + + + + + This request indicates that the client will not use the + river_status_manager object any more. Objects that have been created + through this instance are not affected. + + + + + + This creates a new river_output_status object for the given wl_output. + + + + + + + + This creates a new river_seat_status object for the given wl_seat. + + + + + + + + + This interface allows clients to receive information about the current + windowing state of an output. + + + + + This request indicates that the client will not use the + river_output_status object any more. + + + + + + Sent once binding the interface and again whenever the tag focus of + the output changes. + + + + + + + Sent once on binding the interface and again whenever the tag state + of the output changes. + + + + + + + + This interface allows clients to receive information about the current + focus of a seat. + + + + + This request indicates that the client will not use the + river_seat_status object any more. + + + + + + Sent on binding the interface and again whenever an output gains focus. + + + + + + + Sent whenever an output loses focus. + + + + + + + Sent once on binding the interface and again whenever the focused + view or a property thereof changes. The title may be an empty string + if no view is focused or the focused view did not set a title. + + + + + diff --git a/src/factory.cpp b/src/factory.cpp index f09ea31..5a01d52 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -25,7 +25,12 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { #endif #ifdef HAVE_WLR if (ref == "wlr/taskbar") { - return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]); + return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]); + } +#endif +#ifdef HAVE_RIVER + if (ref == "river/tags") { + return new waybar::modules::river::Tags(id, bar_, config_[name]); } #endif if (ref == "idle_inhibitor") { diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp new file mode 100644 index 0000000..804ea09 --- /dev/null +++ b/src/modules/river/tags.cpp @@ -0,0 +1,118 @@ +#include +#include +#include +#include + +#include "client.hpp" +#include "modules/river/tags.hpp" +#include "river-status-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" + +namespace waybar::modules::river { + +static void listen_focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + uint32_t tags) { + static_cast(data)->handle_focused_tags(tags); +} + +static void listen_view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + struct wl_array *tags) { + static_cast(data)->handle_view_tags(tags); +} + +static const zriver_output_status_v1_listener output_status_listener_impl{ + .focused_tags = listen_focused_tags, + .view_tags = listen_view_tags, +}; + +static void handle_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) { + if (std::strcmp(interface, zriver_status_manager_v1_interface.name) == 0) { + static_cast(data)->status_manager_ = static_cast( + wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, version)); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + /* Ignore event */ +} + +static const wl_registry_listener registry_listener_impl = {.global = handle_global, + .global_remove = handle_global_remove}; + +Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &config) + : waybar::AModule(config, "tags", id, false, false), + status_manager_{nullptr}, + bar_(bar), + box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, + output_status_{nullptr} { + struct wl_display * display = Client::inst()->wl_display; + struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener_impl, this); + wl_display_roundtrip(display); + + if (!status_manager_) { + spdlog::error("river_status_manager_v1 not advertised"); + return; + } + + box_.set_name("tags"); + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + event_box_.add(box_); + + // Default to 9 tags + const uint32_t num_tags = config["num-tags"].isUInt() ? config_["num-tags"].asUInt() : 9; + for (uint32_t tag = 1; tag <= num_tags; ++tag) { + Gtk::Button &button = buttons_.emplace_back(std::to_string(tag)); + button.set_relief(Gtk::RELIEF_NONE); + box_.pack_start(button, false, false, 0); + button.show(); + } + + struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output); + zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this); + + zriver_status_manager_v1_destroy(status_manager_); +} + +Tags::~Tags() { + if (output_status_) { + zriver_output_status_v1_destroy(output_status_); + } +} + +void Tags::handle_focused_tags(uint32_t tags) { + uint32_t i = 0; + for (auto &button : buttons_) { + if ((1 << i) & tags) { + button.get_style_context()->add_class("focused"); + } else { + button.get_style_context()->remove_class("focused"); + } + ++i; + } +} + +void Tags::handle_view_tags(struct wl_array *view_tags) { + // First clear all occupied state + for (auto &button : buttons_) { + button.get_style_context()->remove_class("occupied"); + } + + // Set tags with a view to occupied + uint32_t *start = static_cast(view_tags->data); + for (uint32_t *tags = start; tags < start + view_tags->size / sizeof(uint32_t); ++tags) { + uint32_t i = 0; + for (auto &button : buttons_) { + if (*tags & (1 << i)) { + button.get_style_context()->add_class("occupied"); + } + ++i; + } + } +} + +} /* namespace waybar::modules::river */ From 401ea05dd830e7cf6ef52a03aa6b2dc53587891d Mon Sep 17 00:00:00 2001 From: Jannusch Bigge Date: Tue, 16 Jun 2020 12:30:21 +0200 Subject: [PATCH 008/355] add check that all batteries from type battery --- src/modules/battery.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 406b4a3..beb0554 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -53,8 +53,13 @@ void waybar::modules::Battery::getBatteries() { 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()); + fs::exists(node.path() / "status") && fs::exists(node.path() / "type")) { + std::string type; + std::ifstream(node.path() / "type") >> type; + + if (!type.compare("Battery")){ + batteries_.push_back(node.path()); + } } auto adap_defined = config_["adapter"].isString(); if (((adap_defined && dir_name == config_["adapter"].asString()) || !adap_defined) && From 7429d1f9cc394a20c8d027841a171dd537be5711 Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Wed, 24 Jun 2020 09:36:28 +0200 Subject: [PATCH 009/355] Fix click events for the taskbar module When only the option 'on-click-right' was set and no other 'on-click' option than the taskbar module wouldn't register for click events and hence those events were handled by the generic AModule::on-click code. This code would try to start a shell with the specified command, which wouldn't make any sense in this circumstances. The taskbar code falsely checked for the 'on-click-left' option instead for the 'on-click-right' when deciding to register for click events. --- src/modules/wlr/taskbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 845f07c..361eddc 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -214,7 +214,7 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, /* Handle click events if configured */ if (config_["on-click"].isString() || config_["on-click-middle"].isString() - || config_["on-click-left"].isString()) { + || config_["on-click-right"].isString()) { button_.add_events(Gdk::BUTTON_PRESS_MASK); button_.signal_button_press_event().connect( sigc::mem_fun(*this, &Task::handle_clicked), false); From b7a8e8e5443ffc95811db405a2c7c57d801fafc3 Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Wed, 24 Jun 2020 09:47:34 +0200 Subject: [PATCH 010/355] Fix format option for the taskbar module When using additional format options in addition to {icon} the format is separated into text before and text after the icon. Each of the texts is displayed in a separate label one before and one after the image for the icon. The code updating the labels on changes used the wrong format strings when updating the label after the icon. --- src/modules/wlr/taskbar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 361eddc..f588970 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -187,7 +187,7 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, with_icon_ = true; format_after_ = format.substr(6); } else if (icon_pos == std::string::npos) { - format_after_ = format; + format_before_ = format; } else { with_icon_ = true; format_before_ = format.substr(0, icon_pos); @@ -421,7 +421,7 @@ void Task::update() } if (!format_after_.empty()) { text_after_.set_label( - fmt::format(format_before_, + fmt::format(format_after_, fmt::arg("title", title_), fmt::arg("app_id", app_id_), fmt::arg("state", state_string()), From 732ce7a27cbeb47fb3566768789c14d3123bb06c Mon Sep 17 00:00:00 2001 From: Laurent Arnoud Date: Wed, 24 Jun 2020 13:28:27 +0200 Subject: [PATCH 011/355] chore: always include sway module --- meson.build | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/meson.build b/meson.build index 8d6dc16..3647fbb 100644 --- a/meson.build +++ b/meson.build @@ -155,15 +155,13 @@ elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd ) endif -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 +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' +] if true add_project_arguments('-DHAVE_WLR', language: 'cpp') From 5e044e5bba141c61f6a383032e31df3c7c2a1595 Mon Sep 17 00:00:00 2001 From: Laurent Arnoud Date: Wed, 24 Jun 2020 15:06:30 +0200 Subject: [PATCH 012/355] Remove date macro on version for reproducible builds cf https://reproducible-builds.org/ --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 8d6dc16..addd0e0 100644 --- a/meson.build +++ b/meson.build @@ -36,7 +36,7 @@ else if meson.source_root() == git_path 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) + version = '"@0@ (branch \'@1@\')"'.format(git_commit_hash, git_branch) add_project_arguments('-DVERSION=@0@'.format(version), language: 'cpp') else add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'cpp') From b6067c756998825bc9b40e6dd52ed862976abbd8 Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Thu, 25 Jun 2020 08:05:06 +0200 Subject: [PATCH 013/355] Fix and extend the wlr/taskbar example in the man page The example incorrectly used 'on-middle-click' as option although it should be 'on-click-middle'. Fix this and also add some other options. --- man/waybar-wlr-taskbar.5.scd | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index cbce6e7..68cf02a 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -87,10 +87,12 @@ Addressed by *wlr/taskbar* ``` "wlr/taskbar": { - "format": "{icon}", + "format": "{icon}", + "icon-size": 14, + "icon-theme": "Numix-Circle", "tooltip-format": "{title}", "on-click": "activate", - "on-middle-click": "close" + "on-click-middle": "close" } ``` From 8e6cbc10219f5f73d7d7162d3f7f033ee307d658 Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 1 Jul 2020 02:10:22 +0300 Subject: [PATCH 014/355] Change find icon priority and to_lower app_id --- src/modules/wlr/taskbar.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index f588970..1676235 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -85,9 +85,9 @@ static bool image_load_icon(Gtk::Image& image, Glib::RefPtr icon * send a single app-id, but in any case this works fine */ while (stream >> app_id) { - std::string icon_name = get_from_desktop_app_info(app_id); + std::string icon_name = get_from_icon_theme(icon_theme, app_id); if (icon_name.empty()) - icon_name = get_from_icon_theme(icon_theme, app_id); + icon_name = get_from_desktop_app_info(app_id); if (icon_name.empty()) continue; @@ -271,7 +271,9 @@ void Task::handle_title(const char *title) void Task::handle_app_id(const char *app_id) { app_id_ = app_id; - if (!image_load_icon(icon_, tbar_->icon_theme(), app_id_, + std::transform(app_id_.begin(), app_id_.end(), app_id_.begin(), + [](char c){ return std::tolower(c); }); + if (!image_load_icon(icon_, tbar_->icon_theme(), app_id_, config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16)) spdlog::warn("Failed to load icon for {}", app_id); From 43500a9983a3341bf4309767f597da3c203a2b25 Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 1 Jul 2020 02:56:44 +0300 Subject: [PATCH 015/355] Move lower to load_icon phase --- src/modules/wlr/taskbar.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 1676235..6dd24a7 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -77,6 +77,7 @@ static bool image_load_icon(Gtk::Image& image, Glib::RefPtr icon const std::string &app_id_list, int size) { std::string app_id; + std::string lower_app_id; std::istringstream stream(app_id_list); bool found = false; @@ -85,10 +86,18 @@ static bool image_load_icon(Gtk::Image& image, Glib::RefPtr icon * send a single app-id, but in any case this works fine */ while (stream >> app_id) { + std::transform(app_id.begin(), app_id.end(), lower_app_id.begin(), + [](char c){ return std::tolower(c); }); std::string icon_name = get_from_icon_theme(icon_theme, app_id); + if (icon_name.empty()) + icon_name = get_from_icon_theme(icon_theme, lower_app_id); + if (icon_name.empty()) icon_name = get_from_desktop_app_info(app_id); + if (icon_name.empty()) + icon_name = get_from_desktop_app_info(lower_app_id); + if (icon_name.empty()) continue; @@ -271,8 +280,6 @@ void Task::handle_title(const char *title) void Task::handle_app_id(const char *app_id) { app_id_ = app_id; - std::transform(app_id_.begin(), app_id_.end(), app_id_.begin(), - [](char c){ return std::tolower(c); }); if (!image_load_icon(icon_, tbar_->icon_theme(), app_id_, config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16)) spdlog::warn("Failed to load icon for {}", app_id); From 8a13ed59bc039cb689a48c22f10a9a06d5394636 Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 1 Jul 2020 03:05:17 +0300 Subject: [PATCH 016/355] Fix lower --- src/modules/wlr/taskbar.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 6dd24a7..95a7a51 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -86,11 +86,13 @@ static bool image_load_icon(Gtk::Image& image, Glib::RefPtr icon * send a single app-id, but in any case this works fine */ while (stream >> app_id) { - std::transform(app_id.begin(), app_id.end(), lower_app_id.begin(), - [](char c){ return std::tolower(c); }); std::string icon_name = get_from_icon_theme(icon_theme, app_id); - if (icon_name.empty()) + if (icon_name.empty()) { + lower_app_id = app_id; + std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(), + [](char c){ return std::tolower(c); }); icon_name = get_from_icon_theme(icon_theme, lower_app_id); + } if (icon_name.empty()) icon_name = get_from_desktop_app_info(app_id); From 14bc842e7721cdfa27d275e575efd40f9c37383b Mon Sep 17 00:00:00 2001 From: excellentname Date: Fri, 3 Jul 2020 20:14:01 +1000 Subject: [PATCH 017/355] Fix mpd typo in man page --- man/waybar.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar.5.scd b/man/waybar.5.scd index 9ff1891..5267110 100644 --- a/man/waybar.5.scd +++ b/man/waybar.5.scd @@ -190,7 +190,7 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270. - *waybar-custom(5)* - *waybar-idle-inhibitor(5)* - *waybar-memory(5)* -- *waybar-mdp(5)* +- *waybar-mpd(5)* - *waybar-network(5)* - *waybar-pulseaudio(5)* - *waybar-sway-mode(5)* From b7e8275d90192b38d4a753d54e39ec2cdfd6dabc Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Sun, 5 Jul 2020 13:04:57 +0200 Subject: [PATCH 018/355] Properly trim when splitting up the format string Previously only single spaces would be trimmed and not multiple ones. Now use a proper trim implementation for strings. --- src/modules/wlr/taskbar.cpp | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 95a7a51..520b4b9 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -20,6 +20,27 @@ namespace waybar::modules::wlr { +/* String manipulation methods */ +const std::string WHITESPACE = " \n\r\t\f\v"; + +static std::string ltrim(const std::string& s) +{ + size_t start = s.find_first_not_of(WHITESPACE); + return (start == std::string::npos) ? "" : s.substr(start); +} + +static std::string rtrim(const std::string& s) +{ + size_t end = s.find_last_not_of(WHITESPACE); + return (end == std::string::npos) ? "" : s.substr(0, end + 1); +} + +static std::string trim(const std::string& s) +{ + return rtrim(ltrim(s)); +} + + /* Icon loading functions */ /* Method 1 - get the correct icon name from the desktop file */ @@ -196,13 +217,13 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, auto icon_pos = format.find("{icon}"); if (icon_pos == 0) { with_icon_ = true; - format_after_ = format.substr(6); + format_after_ = trim(format.substr(6)); } else if (icon_pos == std::string::npos) { format_before_ = format; } else { with_icon_ = true; - format_before_ = format.substr(0, icon_pos); - format_after_ = format.substr(icon_pos + 6); + format_before_ = trim(format.substr(0, icon_pos)); + format_after_ = trim(format.substr(icon_pos + 6)); } } else { /* The default is to only show the icon */ @@ -210,11 +231,6 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, } /* Strip spaces at the beginning and end of the format strings */ - if (!format_before_.empty() && format_before_.back() == ' ') - format_before_.pop_back(); - if (!format_after_.empty() && format_after_.front() == ' ') - format_after_.erase(std::cbegin(format_after_)); - format_tooltip_.clear(); if (!config_["tooltip"].isBool() || config_["tooltip"].asBool()) { if (config_["tooltip-format"].isString()) From 06ad35c42b924952dc349469531e03c151d32a6b Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Sun, 5 Jul 2020 13:07:12 +0200 Subject: [PATCH 019/355] Add support for multiple icon themes in the config If there are multiple icon themes defined in the config option 'icon-theme' the module will try from left to right to find an icon. The system default will always be added to this list. --- include/modules/wlr/taskbar.hpp | 4 +-- man/waybar-wlr-taskbar.5.scd | 2 +- src/modules/wlr/taskbar.cpp | 45 ++++++++++++++++++++++++--------- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 5bcb7ec..53a2f8c 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -131,7 +131,7 @@ class Taskbar : public waybar::AModule Gtk::Box box_; std::vector tasks_; - Glib::RefPtr icon_theme_; + std::vector> icon_themes_; struct zwlr_foreign_toplevel_manager_v1 *manager_; struct wl_seat *seat_; @@ -154,7 +154,7 @@ class Taskbar : public waybar::AModule bool show_output(struct wl_output *) const; bool all_outputs() const; - Glib::RefPtr icon_theme() const; + std::vector> icon_themes() const; }; } /* namespace waybar::modules::wlr */ diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 68cf02a..24946e1 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -25,7 +25,7 @@ Addressed by *wlr/taskbar* *icon-theme*: ++ typeof: string ++ - The name of the icon-theme that should be used. If omitted, the system default will be used. + The names of the icon-themes that should be used to find an icon. The list will be traversed from left to right. If omitted, the system default will be used. *icon-size*: ++ typeof: integer ++ diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 520b4b9..f669fa9 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -298,12 +298,23 @@ void Task::handle_title(const char *title) void Task::handle_app_id(const char *app_id) { app_id_ = app_id; - if (!image_load_icon(icon_, tbar_->icon_theme(), app_id_, - config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16)) - spdlog::warn("Failed to load icon for {}", app_id); - if (with_icon_) + if (!with_icon_) + return; + + int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16; + bool found = false; + for (auto& icon_theme : tbar_->icon_themes()) { + if (image_load_icon(icon_, icon_theme, app_id_, icon_size)) { + found = true; + break; + } + } + + if (found) icon_.show(); + else + spdlog::debug("Couldn't find icon for {}", app_id_); } void Task::handle_output_enter(struct wl_output *output) @@ -555,13 +566,23 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu /* Get the configured icon theme if specified */ if (config_["icon-theme"].isString()) { - icon_theme_ = Gtk::IconTheme::create(); - icon_theme_->set_custom_theme(config_["icon-theme"].asString()); - spdlog::debug("Use custom icon theme: {}.", config_["icon-theme"].asString()); - } else { - spdlog::debug("Use system default icon theme"); - icon_theme_ = Gtk::IconTheme::get_default(); + auto icon_theme_config = config_["icon-theme"].asString(); + size_t start = 0, end = 0; + + do { + end = icon_theme_config.find(',', start); + auto it_name = trim(icon_theme_config.substr(start, end-start)); + + auto it = Gtk::IconTheme::create(); + it->set_custom_theme(it_name); + spdlog::debug("Use custom icon theme: {}", it_name); + + icon_themes_.push_back(it); + + start = end == std::string::npos ? end : end + 1; + } while(end != std::string::npos); } + icon_themes_.push_back(Gtk::IconTheme::get_default()); } Taskbar::~Taskbar() @@ -677,9 +698,9 @@ bool Taskbar::all_outputs() const return result; } -Glib::RefPtr Taskbar::icon_theme() const +std::vector> Taskbar::icon_themes() const { - return icon_theme_; + return icon_themes_; } } /* namespace waybar::modules::wlr */ From 7d9f6096fec773b9808b345d8c29b535bc6a8028 Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Sun, 5 Jul 2020 13:10:23 +0200 Subject: [PATCH 020/355] Respect XDG_DATA_DIRS when looking for icons. Use the base folders as defined in $XDG_DATA_DIRS and only fall back to /usr/share and /usr/local/share if the environment variable does not exist. --- src/modules/wlr/taskbar.cpp | 50 +++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index f669fa9..8d28438 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -42,19 +43,43 @@ static std::string trim(const std::string& s) /* Icon loading functions */ +static std::vector search_prefix() +{ + std::vector prefixes = {""}; + + auto xdg_data_dirs = std::getenv("XDG_DATA_DIRS"); + if (!xdg_data_dirs) { + prefixes.push_back("/usr/share/"); + prefixes.push_back("/usr/local/share/"); + } else { + std::string xdg_data_dirs_str(xdg_data_dirs); + size_t start = 0, end = 0; + + do { + end = xdg_data_dirs_str.find(':', start); + auto p = xdg_data_dirs_str.substr(start, end-start); + prefixes.push_back(trim(p) + "/"); + + start = end == std::string::npos ? end : end + 1; + } while(end != std::string::npos); + } + + for (auto& p : prefixes) + spdlog::debug("Using 'desktop' search path prefix: {}", p); + + return prefixes; +} /* Method 1 - get the correct icon name from the desktop file */ static std::string get_from_desktop_app_info(const std::string &app_id) { - Glib::RefPtr app_info; + static std::vector prefixes = search_prefix(); - std::vector prefixes = { + std::vector app_folders = { "", - "/usr/share/applications/", - "/usr/share/applications/kde/", - "/usr/share/applications/org.kde.", - "/usr/local/share/applications/", - "/usr/local/share/applications/org.kde.", + "applications/", + "applications/kde/", + "applications/org.kde." }; std::string lower_app_id = app_id; @@ -72,11 +97,14 @@ static std::string get_from_desktop_app_info(const std::string &app_id) ".desktop" }; + Glib::RefPtr app_info; + for (auto& prefix : prefixes) - for (auto& id : app_id_variations) - for (auto& suffix : suffixes) - if (!app_info) - app_info = Gio::DesktopAppInfo::create_from_filename(prefix + id + suffix); + for (auto& folder : app_folders) + for (auto& id : app_id_variations) + for (auto& suffix : suffixes) + if (!app_info) + app_info = Gio::DesktopAppInfo::create_from_filename(prefix + folder + id + suffix); if (app_info) return app_info->get_icon()->to_string(); From 70e368a1c665ec90b745e20e67a42e8c81eeefe6 Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Tue, 7 Jul 2020 10:39:28 +0200 Subject: [PATCH 021/355] Refactor the lower_app_id logic Move the lower_app_id lookup logic completely in the image_load_icon method and use it also when looking up the icon from the desktop files as well as icon themes. --- src/modules/wlr/taskbar.cpp | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 8d28438..b440dfc 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -82,16 +82,6 @@ static std::string get_from_desktop_app_info(const std::string &app_id) "applications/org.kde." }; - std::string lower_app_id = app_id; - std::transform(std::begin(lower_app_id), std::end(lower_app_id), std::begin(lower_app_id), - [](unsigned char c) { return std::tolower(c); }); - - - std::vector app_id_variations = { - app_id, - lower_app_id - }; - std::vector suffixes = { "", ".desktop" @@ -101,10 +91,9 @@ static std::string get_from_desktop_app_info(const std::string &app_id) for (auto& prefix : prefixes) for (auto& folder : app_folders) - for (auto& id : app_id_variations) - for (auto& suffix : suffixes) - if (!app_info) - app_info = Gio::DesktopAppInfo::create_from_filename(prefix + folder + id + suffix); + for (auto& suffix : suffixes) + if (!app_info) + app_info = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix); if (app_info) return app_info->get_icon()->to_string(); @@ -126,26 +115,23 @@ static bool image_load_icon(Gtk::Image& image, Glib::RefPtr icon const std::string &app_id_list, int size) { std::string app_id; - std::string lower_app_id; std::istringstream stream(app_id_list); bool found = false; - /* Wayfire sends a list of app-id's in space separated format, other compositors * send a single app-id, but in any case this works fine */ while (stream >> app_id) { + auto lower_app_id = app_id; + std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(), + [](char c){ return std::tolower(c); }); + std::string icon_name = get_from_icon_theme(icon_theme, app_id); - if (icon_name.empty()) { - lower_app_id = app_id; - std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(), - [](char c){ return std::tolower(c); }); - icon_name = get_from_icon_theme(icon_theme, lower_app_id); - } + if (icon_name.empty()) + icon_name = get_from_icon_theme(icon_theme, lower_app_id); if (icon_name.empty()) icon_name = get_from_desktop_app_info(app_id); - if (icon_name.empty()) icon_name = get_from_desktop_app_info(lower_app_id); From bc112eaae114403e19145bf64e898c499772385c Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Tue, 7 Jul 2020 11:22:08 +0200 Subject: [PATCH 022/355] Convert icon-theme option to array|string Use the config support of using arrays in its options instead of the complicated and error-prone parsing of the string. --- man/waybar-wlr-taskbar.5.scd | 2 +- src/modules/wlr/taskbar.cpp | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 24946e1..f044412 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -24,7 +24,7 @@ Addressed by *wlr/taskbar* The format, how information should be displayed. *icon-theme*: ++ - typeof: string ++ + typeof: array|string ++ The names of the icon-themes that should be used to find an icon. The list will be traversed from left to right. If omitted, the system default will be used. *icon-size*: ++ diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index b440dfc..23a9166 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -579,22 +579,24 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu } /* Get the configured icon theme if specified */ - if (config_["icon-theme"].isString()) { - auto icon_theme_config = config_["icon-theme"].asString(); - size_t start = 0, end = 0; - - do { - end = icon_theme_config.find(',', start); - auto it_name = trim(icon_theme_config.substr(start, end-start)); + if (config_["icon-theme"].isArray()) { + for (auto& c : config_["icon-theme"]) { + auto it_name = c.asString(); auto it = Gtk::IconTheme::create(); it->set_custom_theme(it_name); spdlog::debug("Use custom icon theme: {}", it_name); icon_themes_.push_back(it); + } + } else if (config_["icon-theme"].isString()) { + auto it_name = config_["icon-theme"].asString(); - start = end == std::string::npos ? end : end + 1; - } while(end != std::string::npos); + auto it = Gtk::IconTheme::create(); + it->set_custom_theme(it_name); + spdlog::debug("Use custom icon theme: {}", it_name); + + icon_themes_.push_back(it); } icon_themes_.push_back(Gtk::IconTheme::get_default()); } From 27f89bdcc32fe42effde28767e3cb33d5c6aaefb Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 8 Jul 2020 22:02:47 +0200 Subject: [PATCH 023/355] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 54c148f..1fa086d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ jobs: env: before_install: - sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf - - sudo pkg install -y date gtk-layer-shell gtkmm30 jsoncpp libdbusmenu + - sudo pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu libfmt libmpdclient libudev-devd meson pulseaudio scdoc spdlog script: - meson build -Dman-pages=enabled From e3fdd6d94a3e3465bd05a032b69fdeeb89744f09 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 12 Jul 2020 11:57:47 +0200 Subject: [PATCH 024/355] fix: don't a warning for a width/height of 0 --- src/bar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index b2e84da..3bbc2a3 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -121,7 +121,7 @@ void waybar::Bar::onConfigure(GdkEventConfigure* ev) { auto tmp_width = width_; if (ev->height > static_cast(height_)) { // Default minimal value - if (height_ != 1) { + if (height_ > 1) { spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); } if (config["height"].isUInt()) { @@ -132,7 +132,7 @@ void waybar::Bar::onConfigure(GdkEventConfigure* ev) { } if (ev->width > static_cast(width_)) { // Default minimal value - if (width_ != 1) { + if (width_ > 1) { spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); } if (config["width"].isUInt()) { From a1f6e386240ee751dc8ae52e6d7a1f1e84d25016 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Thu, 16 Jul 2020 17:03:39 +0000 Subject: [PATCH 025/355] CI: switch FreeBSD to /quarterly packages devel/libffi on /latest was recently updated to 3.3, breaking ABI. However, TravisCI has lang/python37 preinstalled likely from /quarterly. Instead of calling `pkg upgrade -y` or `pkg upgrade -y python37` switch to /quarterly until a newer dependency is required to justify /latest. $ meson build -Dman-pages=enabled [...] ImportError: Shared object "libffi.so.6" not found, required by "_ctypes.so" --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1fa086d..62f7863 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,6 @@ jobs: compiler: clang env: before_install: - - sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf - sudo pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu libfmt libmpdclient libudev-devd meson pulseaudio scdoc spdlog script: From 759602af64e10948af9665813af05237e36ad845 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 18 Jul 2020 10:23:04 +0200 Subject: [PATCH 026/355] Update mpd.cpp --- src/modules/mpd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index 19009cd..957b3c7 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -352,7 +352,7 @@ bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) { } bool waybar::modules::MPD::stopped() { - return connection_ == nullptr || state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP; + return connection_ == nullptr || state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP || status_ == nullptr; } bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; } From 246f7bf555e17dcdb0f765aca3b852382b4075df Mon Sep 17 00:00:00 2001 From: excellentname Date: Tue, 21 Jul 2020 12:36:48 +1000 Subject: [PATCH 027/355] Handle SIGCHLD for exec/forkExec When forkExec is called it begins to ignore all SIGCHLD signals for the rest of the progam's execution so that they are automatically reaped. However, this means that subsequent waitpid calls in the exec function will always fail. So instead handle SIGCHLD by reaping any processes created by forkExec and ignoring all others so that they can be handled directly by the exec function. --- include/util/command.hpp | 12 ++++++++++-- src/main.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/include/util/command.hpp b/include/util/command.hpp index a72a829..9db8d83 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -7,6 +7,9 @@ #include +extern sig_atomic_t is_inserting_pid; +extern std::list reap; + namespace waybar::util::command { struct res { @@ -32,10 +35,11 @@ inline std::string read(FILE* fp) { inline int close(FILE* fp, pid_t pid) { int stat = -1; + pid_t ret; fclose(fp); do { - waitpid(pid, &stat, WCONTINUED | WUNTRACED); + ret = waitpid(pid, &stat, WCONTINUED | WUNTRACED); if (WIFEXITED(stat)) { spdlog::debug("Cmd exited with code {}", WEXITSTATUS(stat)); @@ -45,6 +49,8 @@ inline int close(FILE* fp, pid_t pid) { spdlog::debug("Cmd stopped by {}", WSTOPSIG(stat)); } else if (WIFCONTINUED(stat)) { spdlog::debug("Cmd continued"); + } else if (ret == -1) { + spdlog::debug("waitpid failed: {}", strerror(errno)); } else { break; } @@ -111,7 +117,9 @@ inline int32_t forkExec(const std::string& cmd) { execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); exit(0); } else { - signal(SIGCHLD, SIG_IGN); + is_inserting_pid = true; + reap.push_back(pid); + is_inserting_pid = false; } return pid; diff --git a/src/main.cpp b/src/main.cpp index f066cf8..5350ec0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,32 @@ #include +#include +#include +#include #include #include "client.hpp" +sig_atomic_t is_inserting_pid = false; +std::list reap; + +static void handler(int sig) { + int saved_errno = errno; + if (!is_inserting_pid) { + for (auto it = reap.begin(); it != reap.end(); ++it) { + if (waitpid(*it, nullptr, WNOHANG) == *it) { + it = reap.erase(it); + } + } + } + errno = saved_errno; +} + +inline void installSigChldHandler(void) { + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_handler = handler; + sigaction(SIGCHLD, &sa, nullptr); +} + int main(int argc, char* argv[]) { try { auto client = waybar::Client::inst(); @@ -18,6 +43,7 @@ int main(int argc, char* argv[]) { } }); } + installSigChldHandler(); auto ret = client->main(argc, argv); delete client; From 15fe73a2520e1ca84fbf5fd783cfe0c4bfebe79c Mon Sep 17 00:00:00 2001 From: Michael Rodler Date: Wed, 22 Jul 2020 16:13:24 +0200 Subject: [PATCH 028/355] duplicate the logic to assign numbers to workspaces from sway into waybar to handle perisstent workspaces --- src/modules/sway/workspaces.cpp | 39 ++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 5f7c3f6..fd2ad43 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -1,9 +1,28 @@ #include "modules/sway/workspaces.hpp" +#include +#include #include + namespace waybar::modules::sway { +// this is the code that sway uses to assign a number to a workspace. This is +// taken quite verbatim from `sway/ipc-json.c`. +int sway_wsname_to_num(std::string name) { + if (isdigit(name[0])) { + errno = 0; + char * endptr = NULL; + long long parsed_num = strtoll(name.c_str(), &endptr, 10); + if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == name.c_str()) { + return -1; + } else { + return (int)parsed_num; + } + } + return -1; +} + Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), bar_(bar), @@ -102,13 +121,27 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { // the "num" property (integer type): // The workspace number or -1 for workspaces that do // not start with a number. - auto l = lhs["num"].asInt(); - auto r = rhs["num"].asInt(); + //auto l = lhs["num"].asInt(); + //auto r = rhs["num"].asInt(); + + // We cannot rely on the "num" property as provided by sway + // via IPC, because persistent workspace might not exist in + // sway's view. However, we need this property also for + // not-yet created persistent workspace. As such, we simply + // duplicate sway's logic of assigning the "num" property + // into waybar (see sway_wsname_to_num). This way the + // sorting should work out even when we include workspaces + // that do not currently exist. + auto lname = lhs["name"].asString(); + auto rname = rhs["name"].asString(); + auto l = sway_wsname_to_num(lname); + auto r = sway_wsname_to_num(rname); + if (l == r) { // in case both integers are the same, lexicographical // sort. This also covers the case when both don't have a // number (i.e., l == r == -1). - return lhs["name"].asString() < rhs["name"].asString(); + return lname < rname; } // one of the workspaces doesn't begin with a number, so From c3359dec1b3a711f2912aab6c11009a8c46d2fe2 Mon Sep 17 00:00:00 2001 From: excellentname Date: Sat, 25 Jul 2020 21:02:59 +1000 Subject: [PATCH 029/355] Replace signal handler with signal handling thread --- include/util/command.hpp | 22 ++++++++++--- src/main.cpp | 70 +++++++++++++++++++++++++++++++--------- 2 files changed, 72 insertions(+), 20 deletions(-) diff --git a/include/util/command.hpp b/include/util/command.hpp index 9db8d83..5265558 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -7,7 +7,7 @@ #include -extern sig_atomic_t is_inserting_pid; +extern std::mutex reap_mtx; extern std::list reap; namespace waybar::util::command { @@ -71,6 +71,12 @@ inline FILE* open(const std::string& cmd, int& pid) { } if (!child_pid) { + int err; + sigset_t mask; + sigfillset(&mask); + // Reset sigmask + err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr); + if (err != 0) spdlog::error("pthread_sigmask in open failed: {}", strerror(err)); ::close(fd[0]); dup2(fd[1], 1); setpgid(child_pid, child_pid); @@ -103,7 +109,7 @@ inline struct res execNoRead(const std::string& cmd) { inline int32_t forkExec(const std::string& cmd) { if (cmd == "") return -1; - int32_t pid = fork(); + pid_t pid = fork(); if (pid < 0) { spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno)); @@ -112,14 +118,20 @@ inline int32_t forkExec(const std::string& cmd) { // Child executes the command if (!pid) { + int err; + sigset_t mask; + sigfillset(&mask); + // Reset sigmask + err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr); + if (err != 0) spdlog::error("pthread_sigmask in forkExec failed: {}", strerror(err)); setpgid(pid, pid); - signal(SIGCHLD, SIG_DFL); execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); exit(0); } else { - is_inserting_pid = true; + reap_mtx.lock(); reap.push_back(pid); - is_inserting_pid = false; + reap_mtx.unlock(); + spdlog::debug("Added child to reap list: {}", pid); } return pid; diff --git a/src/main.cpp b/src/main.cpp index 5350ec0..19a8de1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,30 +1,70 @@ #include #include +#include #include #include #include #include "client.hpp" -sig_atomic_t is_inserting_pid = false; +std::mutex reap_mtx; std::list reap; -static void handler(int sig) { - int saved_errno = errno; - if (!is_inserting_pid) { - for (auto it = reap.begin(); it != reap.end(); ++it) { - if (waitpid(*it, nullptr, WNOHANG) == *it) { - it = reap.erase(it); - } +void* signalThread(void* args) { + int err, signum; + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + + while (true) { + err = sigwait(&mask, &signum); + if (err != 0) { + spdlog::error("sigwait failed: {}", strerror(errno)); + continue; + } + + switch (signum) { + case SIGCHLD: + spdlog::debug("Received SIGCHLD in signalThread"); + if (!reap.empty()) { + reap_mtx.lock(); + for (auto it = reap.begin(); it != reap.end(); ++it) { + if (waitpid(*it, nullptr, WNOHANG) == *it) { + spdlog::debug("Reaped child with PID: {}", *it); + it = reap.erase(it); + } + } + reap_mtx.unlock(); + } + break; + default: + spdlog::debug("Received signal with number {}, but not handling", + signum); + break; } } - errno = saved_errno; } -inline void installSigChldHandler(void) { - struct sigaction sa; - sigemptyset(&sa.sa_mask); - sa.sa_handler = handler; - sigaction(SIGCHLD, &sa, nullptr); +void startSignalThread(void) { + int err; + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + + // Block SIGCHLD so it can be handled by the signal thread + // Any threads created by this one (the main thread) should not + // modify their signal mask to unblock SIGCHLD + err = pthread_sigmask(SIG_BLOCK, &mask, nullptr); + if (err != 0) { + spdlog::error("pthread_sigmask failed in startSignalThread: {}", strerror(err)); + exit(1); + } + + pthread_t thread_id; + err = pthread_create(&thread_id, nullptr, signalThread, nullptr); + if (err != 0) { + spdlog::error("pthread_create failed in startSignalThread: {}", strerror(err)); + exit(1); + } } int main(int argc, char* argv[]) { @@ -43,7 +83,7 @@ int main(int argc, char* argv[]) { } }); } - installSigChldHandler(); + startSignalThread(); auto ret = client->main(argc, argv); delete client; From 006850ea5e77419fb857a791d23040bc29d8b182 Mon Sep 17 00:00:00 2001 From: Michael Rodler Date: Mon, 27 Jul 2020 10:56:49 +0200 Subject: [PATCH 030/355] Changed helper function for workspace->num assignment to a static method of Workspaces class and adapted comments/method name to be consistent with the rest --- include/modules/sway/workspaces.hpp | 2 ++ src/modules/sway/workspaces.cpp | 24 +++++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 228c71c..92ec051 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -22,6 +22,8 @@ class Workspaces : public AModule, public sigc::trackable { private: static inline const std::string workspace_switch_cmd_ = "workspace --no-auto-back-and-forth \"{}\""; + static int convertWorkspaceNameToNum(std::string name); + void onCmd(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&); bool filterButtons(); diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index fd2ad43..fc6d5eb 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -1,15 +1,15 @@ #include "modules/sway/workspaces.hpp" -#include -#include #include +#include +#include namespace waybar::modules::sway { -// this is the code that sway uses to assign a number to a workspace. This is -// taken quite verbatim from `sway/ipc-json.c`. -int sway_wsname_to_num(std::string name) { +// Helper function to to assign a number to a workspace, just like sway. In fact +// this is taken quite verbatim from `sway/ipc-json.c`. +int Workspaces::convertWorkspaceNameToNum(std::string name) { if (isdigit(name[0])) { errno = 0; char * endptr = NULL; @@ -121,21 +121,23 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { // the "num" property (integer type): // The workspace number or -1 for workspaces that do // not start with a number. - //auto l = lhs["num"].asInt(); - //auto r = rhs["num"].asInt(); - + // We could rely on sway providing this property: + // + // auto l = lhs["num"].asInt(); + // auto r = rhs["num"].asInt(); + // // We cannot rely on the "num" property as provided by sway // via IPC, because persistent workspace might not exist in // sway's view. However, we need this property also for // not-yet created persistent workspace. As such, we simply // duplicate sway's logic of assigning the "num" property - // into waybar (see sway_wsname_to_num). This way the + // into waybar (see convertWorkspaceNameToNum). This way the // sorting should work out even when we include workspaces // that do not currently exist. auto lname = lhs["name"].asString(); auto rname = rhs["name"].asString(); - auto l = sway_wsname_to_num(lname); - auto r = sway_wsname_to_num(rname); + int l = convertWorkspaceNameToNum(lname); + int r = convertWorkspaceNameToNum(rname); if (l == r) { // in case both integers are the same, lexicographical From 17967da67623a31b388a4ef2fc76f3007e2c96ad Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 28 Jul 2020 20:04:28 +0200 Subject: [PATCH 031/355] Fix 'date' dependency in meson The 'date' library dependency was incompletly imported with meson. Only the target 'date::date' seemed to be caught by meson, but 'date::date-tz' not, which lead to build errors in gentoos sandbox environment. See this question, too: https://stackoverflow.com/questions/62951569/meson-doesnt-link-library Adding the modules in meson imports them all and the library builds nice. Note, that this did not happen with a standard checkout and local build on my system, but only when creating an ebuild for the package. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 5d45a29..dd56c29 100644 --- a/meson.build +++ b/meson.build @@ -98,7 +98,7 @@ 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')) -tz_dep = dependency('date', default_options : [ 'use_system_tzdb=true' ], fallback: [ 'date', 'tz_dep' ]) +tz_dep = dependency('date', default_options : [ 'use_system_tzdb=true' ], modules : [ 'date::date', 'date::date-tz' ], fallback: [ 'date', 'tz_dep' ]) prefix = get_option('prefix') sysconfdir = get_option('sysconfdir') From fb8cda9d903d5a10153f8ece720a0fb7ad3efbd9 Mon Sep 17 00:00:00 2001 From: Eivind Uggedal Date: Fri, 31 Jul 2020 08:15:16 +0200 Subject: [PATCH 032/355] network: make missing nl80211 non-fatal This will enable the networking module to be used for ethernet interfaces on kernels without nl80211 support. It should be reasonable to allow desktop systems without wireless interfaces to run custom kenrel configs without nl80211 compiled in. --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index a288c6a..2c0562f 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -158,7 +158,7 @@ void waybar::modules::Network::createInfoSocket() { } nl80211_id_ = genl_ctrl_resolve(sock_, "nl80211"); if (nl80211_id_ < 0) { - throw std::runtime_error("Can't resolve nl80211 interface"); + spdlog::warn("Can't resolve nl80211 interface"); } } From 74018167ffb135b9d7190e37a4c0dda8a0cbbb6a Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 4 Aug 2020 20:54:14 +0200 Subject: [PATCH 033/355] fix: add missing modules to list in waybar.5 --- man/waybar.5.scd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/waybar.5.scd b/man/waybar.5.scd index 5267110..cd64f7d 100644 --- a/man/waybar.5.scd +++ b/man/waybar.5.scd @@ -185,14 +185,18 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270. - *waybar-backlight(5)* - *waybar-battery(5)* +- *waybar-bluetooth(5)* - *waybar-clock(5)* - *waybar-cpu(5)* - *waybar-custom(5)* +- *waybar-disk(5)* - *waybar-idle-inhibitor(5)* - *waybar-memory(5)* - *waybar-mpd(5)* - *waybar-network(5)* - *waybar-pulseaudio(5)* +- *waybar-river-tags(5)* +- *waybar-states(5)* - *waybar-sway-mode(5)* - *waybar-sway-window(5)* - *waybar-sway-workspaces(5)* From 2b11b7ef8c9b66e9f00ec573fa3de3f14e724e0d Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 5 Aug 2020 02:17:38 +0300 Subject: [PATCH 034/355] Base wlr impl (Manager, Group) --- include/factory.hpp | 1 + include/modules/wlr/workspaces.hpp | 82 ++++++++ meson.build | 1 + protocol/meson.build | 1 + protocol/wlr-workspace-unstable-v1.xml | 270 +++++++++++++++++++++++++ src/factory.cpp | 3 + src/modules/wlr/workspaces.cpp | 152 ++++++++++++++ 7 files changed, 510 insertions(+) create mode 100644 include/modules/wlr/workspaces.hpp create mode 100644 protocol/wlr-workspace-unstable-v1.xml create mode 100644 src/modules/wlr/workspaces.cpp diff --git a/include/factory.hpp b/include/factory.hpp index fcbf3a2..db6fd87 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -9,6 +9,7 @@ #endif #ifdef HAVE_WLR #include "modules/wlr/taskbar.hpp" +#include "modules/wlr/workspaces.hpp" #endif #ifdef HAVE_RIVER #include "modules/river/tags.hpp" diff --git a/include/modules/wlr/workspaces.hpp b/include/modules/wlr/workspaces.hpp new file mode 100644 index 0000000..351ddc4 --- /dev/null +++ b/include/modules/wlr/workspaces.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include + +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "wlr-workspace-unstable-v1-client-protocol.h" + +namespace waybar::modules::wlr { + +class WorkspaceManager; +class WorkspaceGroup; + +class Workspace { + public: + Workspace(const waybar::Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, + zwlr_workspace_handle_v1 &workspace); + ~Workspace() = default; + auto update() -> void; + + enum class State { ACTIVE = 1 << 1 }; + + private: + const Bar & bar_; + const Gtk::Box box_; + const Gtk::Image icon_; + const Json::Value & config_; + const WorkspaceGroup &workspace_group_; + + // wlr stuff + zwlr_workspace_handle_v1 &workspace_handle_; + uint32_t state_; +}; + +class WorkspaceGroup { + public: + WorkspaceGroup(const waybar::Bar &bar, const Json::Value &config, WorkspaceManager &manager, + zwlr_workspace_group_handle_v1 &workspace_group_handle); + auto update() -> void; + + // wlr stuff + auto handle_workspace_create(zwlr_workspace_handle_v1 &workspace_handle) -> void; + auto handle_remove() -> void; + auto handle_output_enter(wl_output &output) -> void; + auto handle_output_leave(wl_output &output) -> void; + + private: + const waybar::Bar & bar_; + const Json::Value & config_; + const WorkspaceManager & workspace_manager_; + std::vector> workspaces_; + + // wlr stuff + zwlr_workspace_group_handle_v1 &workspace_group_handle_; +}; + +class WorkspaceManager : public AModule { + public: + WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config); + auto update() -> void override; + + // wlr stuff + auto register_manager(wl_registry *registry, uint32_t name, uint32_t version) -> void; + auto handle_workspace_group_create(zwlr_workspace_group_handle_v1 *workspace_group_handle) + -> void; + auto handle_done() -> void; + auto handle_finished() -> void; + + private: + const waybar::Bar & bar_; + Gtk::Box box_; + std::vector> groups_; + + // wlr stuff + zwlr_workspace_manager_v1 *workspace_manager_ = nullptr; +}; + +} // namespace waybar::modules::wlr diff --git a/meson.build b/meson.build index dd56c29..d0ba1e8 100644 --- a/meson.build +++ b/meson.build @@ -166,6 +166,7 @@ src_files += [ if true add_project_arguments('-DHAVE_WLR', language: 'cpp') src_files += 'src/modules/wlr/taskbar.cpp' + src_files += 'src/modules/wlr/workspaces.cpp' endif if true diff --git a/protocol/meson.build b/protocol/meson.build index 07d524a..467260b 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -27,6 +27,7 @@ client_protocols = [ [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['wlr-foreign-toplevel-management-unstable-v1.xml'], + ['wlr-workspace-unstable-v1.xml'], ['river-status-unstable-v1.xml'], ] diff --git a/protocol/wlr-workspace-unstable-v1.xml b/protocol/wlr-workspace-unstable-v1.xml new file mode 100644 index 0000000..ae0e44d --- /dev/null +++ b/protocol/wlr-workspace-unstable-v1.xml @@ -0,0 +1,270 @@ + + + + Copyright © 2019 Christopher Billington + Copyright © 2020 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Workspaces, also called virtual desktops, are groups of surfaces. A + compositor with a concept of workspaces may only show some such groups of + surfaces (those of 'active' workspaces) at a time. 'Activating' a + workspace is a request for the compositor to display that workspace's + surfaces as normal, whereas the compositor may hide or otherwise + de-emphasise surfaces that are associated only with 'inactive' workspaces. + Workspaces are grouped by which sets of outputs they correspond to, and + may contain surfaces only from those outputs. In this way, it is possible + for each output to have its own set of workspaces, or for all outputs (or + any other arbitrary grouping) to share workspaces. Compositors may + optionally conceptually arrange each group of workspaces in an + N-dimensional grid. + + The purpose of this protocol is to enable the creation of taskbars and + docks by providing them with a list of workspaces and their properties, + and allowing them to activate and deactivate workspaces. + + After a client binds the zwlr_workspace_manager_v1, each workspace will be + sent via the workspace event. + + + + + This event is emitted whenever a new workspace group has been created. + + All initial details of the workspace group (workspaces, outputs) will be + sent immediately after this event via the corresponding events in + zwlr_workspace_group_handle_v1. + + + + + + + The client must send this request after it has finished sending other + requests. The compositor must process a series of requests preceding a + commit request atomically. + + This allows changes to the workspace properties to be seen as atomic, + even if they happen via multiple events, and even if they involve + multiple zwlr_workspace_handle_v1 objects, for example, deactivating one + workspace and activating another. + + + + + + This event is sent after all changes in all workspace groups have been + sent. + + This allows changes to one or more zwlr_workspace_group_handle_v1 + properties to be seen as atomic, even if they happen via multiple + events. In particular, an output moving from one workspace group to + another sends an output_enter event and an output_leave event to the two + zwlr_workspace_group_handle_v1 objects in question. The compositor sends + the done event only after updating the output information in both + workspace groups. + + + + + + This event indicates that the compositor is done sending events to the + zwlr_workspace_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + Indicates the client no longer wishes to receive events for new + workspace groups. However the compositor may emit further workspace + events, until the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + + A zwlr_workspace_group_handle_v1 object represents a a workspace group + that is assigned a set of outputs and contains a number of workspaces. + + The set of outputs assigned to the workspace group is conveyed to the client via + output_enter and output_leave events, and its workspaces are conveyed with + workspace events. + + + + + This event is emitted whenever an output is assigned to the workspace + group. + + + + + + + This event is emitted whenever an output is removed from the workspace + group. + + + + + + + This event is emitted whenever a new workspace has been created. + + All initial details of the workspace (name, coordinates, state) will + be sent immediately after this event via the corresponding events in + zwlr_workspace_handle_v1. + + + + + + + This event means the zwlr_workspace_group_handle_v1 has been destroyed. + It is guaranteed there won't be any more events for this + zwlr_workspace_group_handle_v1. The zwlr_workspace_group_handle_v1 becomes + inert so any requests will be ignored except the destroy request. + + The compositor must remove all workspaces belonging to a workspace group + before removing the workspace group. + + + + + + Destroys the zwlr_workspace_handle_v1 object. + + This request should be called either when the client does not want to + use the workspace object any more or after the remove event to finalize + the destruction of the object. + + + + + + + A zwlr_workspace_handle_v1 object represents a a workspace that handles a + group of surfaces. + + Each workspace has a name, conveyed to the client with the name event; a + list of states, conveyed to the client with the state event; and + optionally a set of coordinates, conveyed to the client with the + coordinates event. The client may request that the compositor activate or + deactivate the workspace. + + + + + This event is emitted immediately after the zwlr_workspace_handle_v1 is + created and whenever the name of the workspace changes. + + + + + + + This event is used to organize workspaces into an N-dimensional grid + within a workspace group, and if supported, is emitted immediately after + the zwlr_workspace_handle_v1 is created and whenever the coordinates of + the workspace change. Compositors may not send this event if they do not + conceptually arrange workspaces in this way. If compositors simply + number workspaces, without any geometric interpretation, they may send + 1D coordinates, which clients should not interpret as implying any + geometry. Sending an empty array means that the compositor no longer + orders the workspace geometrically. + + Coordinates have an arbitrary number of dimensions N with an uint32 + position along each dimension. By convention if N > 1, the first + dimension is X, the second Y, the third Z, and so on. The compositor may + chose to utilize these events for a more novel workspace layout + convention, however. No guarantee is made about the grid being filled or + bounded; there may be a workspace at coordinate 1 and another at + coordinate 1000 and none in between. Within a workspace group, however, + workspaces must have unique coordinates of equal dimensionality. + + + + + + + This event is emitted immediately after the zwlr_workspace_handle_v1 is + created and each time the workspace state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + The different states that a workspace can have. + + + + + + + + This event means the zwlr_workspace_handle_v1 has been destroyed. It is + guaranteed there won't be any more events for this + zwlr_workspace_handle_v1. The zwlr_workspace_handle_v1 becomes inert so + any requests will be ignored except the destroy request. + + + + + + Destroys the zwlr_workspace_handle_v1 object. + + This request should be called either when the client does not want to + use the workspace object any more or after the remove event to finalize + the destruction of the object. + + + + + + Request that this workspace be activated. + + There is no guarantee the workspace will be actually activated, and + behaviour may be compositor-dependent. For example, activating a + workspace may or may not deactivate all other workspaces in the same + group. + + + + + + Request that this workspace be deactivated. + + There is no guarantee the workspace will be actually deactivated. + + + + diff --git a/src/factory.cpp b/src/factory.cpp index 5a01d52..804378e 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -27,6 +27,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "wlr/taskbar") { return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]); } + if (ref == "wlr/workspaces") { + return new waybar::modules::wlr::WorkspaceManager(id, bar_, config_[name]); + } #endif #ifdef HAVE_RIVER if (ref == "river/tags") { diff --git a/src/modules/wlr/workspaces.cpp b/src/modules/wlr/workspaces.cpp new file mode 100644 index 0000000..0d9ae0b --- /dev/null +++ b/src/modules/wlr/workspaces.cpp @@ -0,0 +1,152 @@ +#include "modules/wlr/workspaces.hpp" + +#include +#include + +#include + +namespace waybar::modules::wlr { + +static void handle_global(void *data, wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) { + if (std::strcmp(interface, zwlr_workspace_manager_v1_interface.name) == 0) { + static_cast(data)->register_manager(registry, name, version); + } +} + +static void handle_global_remove(void *data, wl_registry *registry, uint32_t name) { + /* Nothing to do here */ +} + +static const wl_registry_listener registry_listener_impl = {.global = handle_global, + .global_remove = handle_global_remove}; + +WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar, + const Json::Value &config) + : waybar::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_); + + // wlr stuff + wl_display * display = Client::inst()->wl_display; + wl_registry *registry = wl_display_get_registry(display); + + wl_registry_add_listener(registry, ®istry_listener_impl, this); + wl_display_roundtrip(display); + + if (!workspace_manager_) { + return; + } +} + +static void workspace_manager_handle_workspace_group( + void *data, zwlr_workspace_manager_v1 *_, + zwlr_workspace_group_handle_v1 *workspace_group) { + static_cast(data)->handle_workspace_group_create(workspace_group); +} + +static void workspace_manager_handle_done(void *data, zwlr_workspace_manager_v1 *_) { + static_cast(data)->handle_done(); +} + +static void workspace_manager_handle_finished(void *data, zwlr_workspace_manager_v1 *_) { + static_cast(data)->handle_finished(); +} + +static const zwlr_workspace_manager_v1_listener workspace_manager_impl = { + .workspace_group = workspace_manager_handle_workspace_group, + .done = workspace_manager_handle_done, + .finished = workspace_manager_handle_finished, +}; + +auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) + -> void { + if (workspace_manager_) { + spdlog::warn("Register workspace manager again although already registered!"); + return; + } + if (version != 1) { + spdlog::warn("Using different workspace manager protocol version: {}", version); + } + + workspace_manager_ = static_cast( + wl_registry_bind(registry, name, &zwlr_workspace_manager_v1_interface, version)); + + if (workspace_manager_) + zwlr_workspace_manager_v1_add_listener(workspace_manager_, &workspace_manager_impl, this); + else + spdlog::debug("Failed to register manager"); +} +auto WorkspaceManager::handle_workspace_group_create( + zwlr_workspace_group_handle_v1 *workspace_group_handle) -> void { + groups_.push_back(std::make_unique(bar_, config_, *this, *workspace_group_handle)); +} + +auto WorkspaceManager::handle_finished() -> void { + zwlr_workspace_manager_v1_destroy(workspace_manager_); + workspace_manager_ = nullptr; +} +auto WorkspaceManager::handle_done() -> void {} +auto WorkspaceManager::update() -> void { + for (auto &group : groups_) { + group->update(); + } + AModule::update(); +} + +static void workspace_group_handle_output_enter(void *data, + zwlr_workspace_group_handle_v1 *_, + wl_output *output) { + static_cast(data)->handle_output_enter(*output); +} + +static void workspace_group_handle_output_leave(void *data, + zwlr_workspace_group_handle_v1 *_, + wl_output *output) { + static_cast(data)->handle_output_leave(*output); +} + +static void workspace_group_handle_workspace(void *data, + zwlr_workspace_group_handle_v1 *_, + zwlr_workspace_handle_v1 *workspace) { + static_cast(data)->handle_workspace_create(*workspace); +} + +static void workspace_group_handle_remove(void *data, + zwlr_workspace_group_handle_v1 *_) { + static_cast(data)->handle_remove(); +} + +static const zwlr_workspace_group_handle_v1_listener workspace_group_impl = { + .output_enter = workspace_group_handle_output_enter, + .output_leave = workspace_group_handle_output_leave, + .workspace = workspace_group_handle_workspace, + .remove = workspace_group_handle_remove +}; + +WorkspaceGroup::WorkspaceGroup(const Bar & bar, const Json::Value &config, WorkspaceManager &manager, + zwlr_workspace_group_handle_v1 &workspace_group_handle) + : bar_(bar), config_(config), workspace_manager_(manager), workspace_group_handle_(workspace_group_handle) +{ + zwlr_workspace_group_handle_v1_add_listener(&workspace_group_handle, &workspace_group_impl, this); +} +auto WorkspaceGroup::handle_workspace_create(zwlr_workspace_handle_v1 &workspace) -> void { + workspaces_.push_back(std::make_unique(bar_, config_, *this, workspace)); +} +auto WorkspaceGroup::handle_remove() -> void {} +auto WorkspaceGroup::handle_output_enter(wl_output &output) -> void {} +auto WorkspaceGroup::handle_output_leave(wl_output &output) -> void {} +auto WorkspaceGroup::update() -> void { + +} +Workspace::Workspace(const Bar &bar, const Json::Value &config, + WorkspaceGroup &workspace_group, zwlr_workspace_handle_v1 &workspace) + : bar_(bar), config_(config), workspace_group_(workspace_group), workspace_handle_(workspace) +{ +} +} // namespace waybar::modules::wlr \ No newline at end of file From 40d3f1c1feee28d34390f99b36edb9da504ea4b1 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 5 Aug 2020 09:42:04 -0700 Subject: [PATCH 035/355] chore(subprojects): update date to 3.0.0 Fixes #776, fixes #780 --- subprojects/date.wrap | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/subprojects/date.wrap b/subprojects/date.wrap index ea73f0f..4d4067c 100644 --- a/subprojects/date.wrap +++ b/subprojects/date.wrap @@ -1,9 +1,9 @@ [wrap-file] -source_url=https://github.com/HowardHinnant/date/archive/v2.4.1.tar.gz -source_filename=date-2.4.1.tar.gz -source_hash=98907d243397483bd7ad889bf6c66746db0d7d2a39cc9aacc041834c40b65b98 -directory=date-2.4.1 +source_url=https://github.com/HowardHinnant/date/archive/v3.0.0.tar.gz +source_filename=date-3.0.0.tar.gz +source_hash=87bba2eaf0ebc7ec539e5e62fc317cb80671a337c1fb1b84cb9e4d42c6dbebe3 +directory=date-3.0.0 -patch_url = https://github.com/mesonbuild/hinnant-date/releases/download/2.4.1-1/hinnant-date.zip -patch_filename = hinnant-date-2.4.1-1-wrap.zip -patch_hash = 2061673a6f8e6d63c3a40df4da58fa2b3de2835fd9b3e74649e8279599f3a8f6 +patch_url = https://github.com/mesonbuild/hinnant-date/releases/download/3.0.0-1/hinnant-date.zip +patch_filename = hinnant-date-3.0.0-1-wrap.zip +patch_hash = 6ccaf70732d8bdbd1b6d5fdf3e1b935c23bf269bda12fdfd0e561276f63432fe From 66aa3574d9c3a5696889f5b2f9718b7606472404 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 5 Aug 2020 09:44:58 -0700 Subject: [PATCH 036/355] chore(subprojects): update gtk-layer-shell to 0.2.0 Fixes: #530, fixes #750 --- subprojects/gtk-layer-shell.wrap | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/subprojects/gtk-layer-shell.wrap b/subprojects/gtk-layer-shell.wrap index b826ab9..9db9df5 100644 --- a/subprojects/gtk-layer-shell.wrap +++ b/subprojects/gtk-layer-shell.wrap @@ -1,5 +1,5 @@ [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 +directory = gtk-layer-shell-0.2.0 +source_filename = gtk-layer-shell-0.2.0.tar.gz +source_hash = 6934376b5296d079fca2c1ba6b222ec91db6bf3667142340ee2bdebfb4b69116 +source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.2.0/gtk-layer-shell-0.2.0.tar.gz From dcc0201b45efa2e6327ac65cfd63f7006a0d0f78 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 5 Aug 2020 09:39:16 -0700 Subject: [PATCH 037/355] chore(protocol): update wlr-layer-shell-unstable-v1 protocol. Statically linked gtk-layer-shell would use layer-shell protocol object file from waybar and print runtime warning if the version does not match --- protocol/wlr-layer-shell-unstable-v1.xml | 52 ++++++++++++++++++------ 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/protocol/wlr-layer-shell-unstable-v1.xml b/protocol/wlr-layer-shell-unstable-v1.xml index fb4f6b2..f9a4fe0 100644 --- a/protocol/wlr-layer-shell-unstable-v1.xml +++ b/protocol/wlr-layer-shell-unstable-v1.xml @@ -25,7 +25,7 @@ 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 @@ -82,17 +82,27 @@ + + + + + + This request indicates that the client will not use the layer_shell + object any more. Objects that have been created through this instance + are not affected. + + - + 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. + Layer surface state (layer, 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. @@ -115,7 +125,7 @@ Requests that the compositor anchor the surface to the specified edges - and corners. If two orthoginal edges are specified (e.g. 'top' and + and corners. If two orthogonal edges are specified (e.g. 'top' and 'left'), then the anchor point will be the intersection of the edges (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. @@ -127,20 +137,25 @@ - Requests that the compositor avoids occluding an area of the surface - with other surfaces. The compositor's use of this information is + Requests that the compositor avoids occluding an area 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. + A positive value is only meaningful if the surface is anchored to one + edge or an edge and both perpendicular edges. If the surface is not + anchored, anchored to only two perpendicular edges (a corner), anchored + to only two parallel edges or anchored to all edges, a positive value + will be treated the same as zero. + + A positive zone is the distance from the edge in surface-local + coordinates to consider exclusive. Surfaces that do not wish to have an exclusive zone may instead specify 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 with a positive exclusive zone. If set to -1, the surface + indicates that it would not like to be moved to accommodate for other surfaces, and the compositor should extend it all the way to the edges it is anchored to. @@ -281,5 +296,16 @@ + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + From edd4d8ee119f1ebeb51ecbac814784e8980e08ca Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 5 Aug 2020 23:10:08 +0300 Subject: [PATCH 038/355] Finish implement wlr --- include/factory.hpp | 2 +- include/modules/wlr/workspace_manager.hpp | 109 +++++++++++ .../modules/wlr/workspace_manager_binding.hpp | 8 + include/modules/wlr/workspaces.hpp | 82 -------- meson.build | 3 +- src/modules/wlr/workspace_manager.cpp | 182 ++++++++++++++++++ src/modules/wlr/workspace_manager_binding.cpp | 134 +++++++++++++ src/modules/wlr/workspaces.cpp | 152 --------------- 8 files changed, 436 insertions(+), 236 deletions(-) create mode 100644 include/modules/wlr/workspace_manager.hpp create mode 100644 include/modules/wlr/workspace_manager_binding.hpp delete mode 100644 include/modules/wlr/workspaces.hpp create mode 100644 src/modules/wlr/workspace_manager.cpp create mode 100644 src/modules/wlr/workspace_manager_binding.cpp delete mode 100644 src/modules/wlr/workspaces.cpp diff --git a/include/factory.hpp b/include/factory.hpp index db6fd87..a34e361 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -9,7 +9,7 @@ #endif #ifdef HAVE_WLR #include "modules/wlr/taskbar.hpp" -#include "modules/wlr/workspaces.hpp" +#include "modules/wlr/workspace_manager.hpp" #endif #ifdef HAVE_RIVER #include "modules/river/tags.hpp" diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp new file mode 100644 index 0000000..3c8fe99 --- /dev/null +++ b/include/modules/wlr/workspace_manager.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include +#include + +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "wlr-workspace-unstable-v1-client-protocol.h" + +namespace waybar::modules::wlr { + +class WorkspaceManager; +class WorkspaceGroup; + +class Workspace { + public: + Workspace(const waybar::Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, + zwlr_workspace_handle_v1 *workspace); + ~Workspace(); + auto update() -> void; + + auto id() const -> uint32_t { return id_; } + auto handle_name(const std::string &name) -> void { name_ = name; } + auto handle_coordinates(const std::vector &coordinates) -> void { + coordinates_ = coordinates; + } + auto handle_state(const std::vector &state) -> void; + auto handle_remove() -> void; + + enum class State { ACTIVE = 1 << 0 }; + + private: + static uint32_t workspace_global_id; + const Bar & bar_; + const Json::Value &config_; + WorkspaceGroup & workspace_group_; + + // wlr stuff + zwlr_workspace_handle_v1 *workspace_handle_; + uint32_t state_ = 0; + + uint32_t id_; + std::string name_; + std::vector coordinates_; + + const Gtk::Box box_; + const Gtk::Image icon_; +}; + +class WorkspaceGroup { + public: + WorkspaceGroup(const waybar::Bar &bar, const Json::Value &config, WorkspaceManager &manager, + zwlr_workspace_group_handle_v1 *workspace_group_handle); + ~WorkspaceGroup(); + auto update() -> void; + + auto id() const -> uint32_t { return id_; } + auto remove_workspace(uint32_t id_) -> void; + + // wlr stuff + auto handle_workspace_create(zwlr_workspace_handle_v1 *workspace_handle) -> void; + + auto handle_remove() -> void; + auto handle_output_enter(wl_output *output) -> void; + auto handle_output_leave() -> void; + + private: + static uint32_t group_global_id; + const waybar::Bar &bar_; + const Json::Value &config_; + WorkspaceManager & workspace_manager_; + + // wlr stuff + zwlr_workspace_group_handle_v1 *workspace_group_handle_; + wl_output * output_ = nullptr; + + uint32_t id_; + std::vector> workspaces_; +}; + +class WorkspaceManager : public AModule { + public: + WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config); + ~WorkspaceManager() override; + auto update() -> void override; + + auto remove_workspace_group(uint32_t id_) -> void; + + // wlr stuff + auto register_manager(wl_registry *registry, uint32_t name, uint32_t version) -> void; + auto handle_workspace_group_create(zwlr_workspace_group_handle_v1 *workspace_group_handle) + -> void; + + auto handle_done() -> void; + auto handle_finished() -> void; + + private: + const waybar::Bar & bar_; + Gtk::Box box_; + std::vector> groups_; + + // wlr stuff + zwlr_workspace_manager_v1 *workspace_manager_ = nullptr; +}; + +} // namespace waybar::modules::wlr diff --git a/include/modules/wlr/workspace_manager_binding.hpp b/include/modules/wlr/workspace_manager_binding.hpp new file mode 100644 index 0000000..1cef652 --- /dev/null +++ b/include/modules/wlr/workspace_manager_binding.hpp @@ -0,0 +1,8 @@ +#include "wlr-workspace-unstable-v1-client-protocol.h" + +namespace waybar::modules::wlr { + void add_registry_listener(void *data); + void add_workspace_listener(zwlr_workspace_handle_v1 *workspace_handle, void *data); + void add_workspace_group_listener(zwlr_workspace_group_handle_v1 *workspace_group_handle, void *data); + zwlr_workspace_manager_v1* workspace_manager_bind(wl_registry *registry, uint32_t name, uint32_t version, void *data); +} \ No newline at end of file diff --git a/include/modules/wlr/workspaces.hpp b/include/modules/wlr/workspaces.hpp deleted file mode 100644 index 351ddc4..0000000 --- a/include/modules/wlr/workspaces.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#include "AModule.hpp" -#include "bar.hpp" -#include "wlr-workspace-unstable-v1-client-protocol.h" - -namespace waybar::modules::wlr { - -class WorkspaceManager; -class WorkspaceGroup; - -class Workspace { - public: - Workspace(const waybar::Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, - zwlr_workspace_handle_v1 &workspace); - ~Workspace() = default; - auto update() -> void; - - enum class State { ACTIVE = 1 << 1 }; - - private: - const Bar & bar_; - const Gtk::Box box_; - const Gtk::Image icon_; - const Json::Value & config_; - const WorkspaceGroup &workspace_group_; - - // wlr stuff - zwlr_workspace_handle_v1 &workspace_handle_; - uint32_t state_; -}; - -class WorkspaceGroup { - public: - WorkspaceGroup(const waybar::Bar &bar, const Json::Value &config, WorkspaceManager &manager, - zwlr_workspace_group_handle_v1 &workspace_group_handle); - auto update() -> void; - - // wlr stuff - auto handle_workspace_create(zwlr_workspace_handle_v1 &workspace_handle) -> void; - auto handle_remove() -> void; - auto handle_output_enter(wl_output &output) -> void; - auto handle_output_leave(wl_output &output) -> void; - - private: - const waybar::Bar & bar_; - const Json::Value & config_; - const WorkspaceManager & workspace_manager_; - std::vector> workspaces_; - - // wlr stuff - zwlr_workspace_group_handle_v1 &workspace_group_handle_; -}; - -class WorkspaceManager : public AModule { - public: - WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config); - auto update() -> void override; - - // wlr stuff - auto register_manager(wl_registry *registry, uint32_t name, uint32_t version) -> void; - auto handle_workspace_group_create(zwlr_workspace_group_handle_v1 *workspace_group_handle) - -> void; - auto handle_done() -> void; - auto handle_finished() -> void; - - private: - const waybar::Bar & bar_; - Gtk::Box box_; - std::vector> groups_; - - // wlr stuff - zwlr_workspace_manager_v1 *workspace_manager_ = nullptr; -}; - -} // namespace waybar::modules::wlr diff --git a/meson.build b/meson.build index d0ba1e8..638a766 100644 --- a/meson.build +++ b/meson.build @@ -166,7 +166,8 @@ src_files += [ if true add_project_arguments('-DHAVE_WLR', language: 'cpp') src_files += 'src/modules/wlr/taskbar.cpp' - src_files += 'src/modules/wlr/workspaces.cpp' + src_files += 'src/modules/wlr/workspace_manager.cpp' + src_files += 'src/modules/wlr/workspace_manager_binding.cpp' endif if true diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp new file mode 100644 index 0000000..04a3188 --- /dev/null +++ b/src/modules/wlr/workspace_manager.cpp @@ -0,0 +1,182 @@ +#include "modules/wlr/workspace_manager.hpp" + +#include +#include + +#include + +#include "modules/wlr/workspace_manager_binding.hpp" + +namespace waybar::modules::wlr { + +uint32_t Workspace::workspace_global_id = 0; +uint32_t WorkspaceGroup::group_global_id = 0; + +WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar, + const Json::Value &config) + : waybar::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_); + + add_registry_listener(this); + if (!workspace_manager_) { + return; + } +} + +auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) + -> void { + if (workspace_manager_) { + spdlog::warn("Register workspace manager again although already registered!"); + return; + } + if (version != 1) { + spdlog::warn("Using different workspace manager protocol version: {}", version); + } + workspace_manager_ = workspace_manager_bind(registry, name, version, this); +} + +auto WorkspaceManager::handle_workspace_group_create( + zwlr_workspace_group_handle_v1 *workspace_group_handle) -> void { + groups_.push_back(std::make_unique(bar_, config_, *this, workspace_group_handle)); + spdlog::debug("Workspace group {} created", groups_.back()->id()); +} + +auto WorkspaceManager::handle_finished() -> void { + zwlr_workspace_manager_v1_destroy(workspace_manager_); + workspace_manager_ = nullptr; +} + +auto WorkspaceManager::handle_done() -> void { +} + +auto WorkspaceManager::update() -> void { + for (auto &group : groups_) { + group->update(); + } + AModule::update(); +} + +WorkspaceManager::~WorkspaceManager() { + if (!workspace_manager_) { + return; + } + + zwlr_workspace_manager_v1_destroy(workspace_manager_); + workspace_manager_ = nullptr; +} +auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void { + auto it = std::find_if(groups_.begin(), + groups_.end(), + [id](const std::unique_ptr &g) { return g->id() == id; }); + + if (it == groups_.end()) { + spdlog::warn("Can't find group with id {}", id); + return; + } + + groups_.erase(it); +} + +WorkspaceGroup::WorkspaceGroup(const Bar &bar, const Json::Value &config, WorkspaceManager &manager, + zwlr_workspace_group_handle_v1 *workspace_group_handle) + : bar_(bar), + config_(config), + workspace_manager_(manager), + workspace_group_handle_(workspace_group_handle), + id_(++group_global_id) { + add_workspace_group_listener(workspace_group_handle, this); +} + +WorkspaceGroup::~WorkspaceGroup()->void { + if (!workspace_group_handle_) { + return; + } + + zwlr_workspace_group_handle_v1_destroy(workspace_group_handle_); + workspace_group_handle_ = nullptr; +} + +auto WorkspaceGroup::handle_workspace_create(zwlr_workspace_handle_v1 *workspace) -> void { + workspaces_.push_back(std::make_unique(bar_, config_, *this, workspace)); + spdlog::debug("Workspace {} created", workspaces_.back()->id()); +} + +auto WorkspaceGroup::handle_remove() -> void { + zwlr_workspace_group_handle_v1_destroy(workspace_group_handle_); + workspace_group_handle_ = nullptr; + workspace_manager_.remove_workspace_group(id_); +} + +auto WorkspaceGroup::handle_output_enter(wl_output *output) -> void { + spdlog::debug("Output {} assigned to {} group", (void *)output, id_); + output_ = output; +} + +auto WorkspaceGroup::handle_output_leave() -> void { + spdlog::debug("Output {} remove from {} group", (void *)output_, id_); + output_ = nullptr; +} + +auto WorkspaceGroup::update() -> void { + for (auto &workspace : workspaces_) { + workspace->update(); + } +} + +auto WorkspaceGroup::remove_workspace(uint32_t id) -> void { + auto it = std::find_if(workspaces_.begin(), + workspaces_.end(), + [id](const std::unique_ptr &w) { return w->id() == id; }); + + if (it == workspaces_.end()) { + spdlog::warn("Can't find group with id {}", id); + return; + } + + workspaces_.erase(it); +} + +Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, + zwlr_workspace_handle_v1 *workspace) + : bar_(bar), + config_(config), + workspace_group_(workspace_group), + workspace_handle_(workspace), + id_(++workspace_global_id) { + add_workspace_listener(workspace, this); +} + +Workspace::~Workspace() { + if (!workspace_handle_) { + return; + } + + zwlr_workspace_handle_v1_destroy(workspace_handle_); + workspace_handle_ = nullptr; +} + +auto Workspace::update() -> void {} + +auto Workspace::handle_state(const std::vector &state) -> void { + state_ = 0; + for (auto state_entry : state) { + switch (state_entry) { + case ZWLR_WORKSPACE_HANDLE_V1_STATE_ACTIVE: + state_ |= (uint32_t)State::ACTIVE; + break; + } + } +} + +auto Workspace::handle_remove() -> void { + zwlr_workspace_handle_v1_destroy(workspace_handle_); + workspace_handle_ = nullptr; + workspace_group_.remove_workspace(id_); +} +} // namespace waybar::modules::wlr \ No newline at end of file diff --git a/src/modules/wlr/workspace_manager_binding.cpp b/src/modules/wlr/workspace_manager_binding.cpp new file mode 100644 index 0000000..f6db711 --- /dev/null +++ b/src/modules/wlr/workspace_manager_binding.cpp @@ -0,0 +1,134 @@ +#include "modules/wlr/workspace_manager_binding.hpp" + +#include + +#include "client.hpp" +#include "modules/wlr/workspace_manager.hpp" + +namespace waybar::modules::wlr { + +static void handle_global(void *data, wl_registry *registry, uint32_t name, const char *interface, + uint32_t version) { + if (std::strcmp(interface, zwlr_workspace_manager_v1_interface.name) == 0) { + static_cast(data)->register_manager(registry, name, version); + } +} + +static void handle_global_remove(void *data, wl_registry *registry, uint32_t name) { + /* Nothing to do here */ +} + +static const wl_registry_listener registry_listener_impl = {.global = handle_global, + .global_remove = handle_global_remove}; + +void add_registry_listener(void *data) { + wl_display * display = Client::inst()->wl_display; + wl_registry *registry = wl_display_get_registry(display); + + wl_registry_add_listener(registry, ®istry_listener_impl, data); + wl_display_roundtrip(display); + wl_display_roundtrip(display); +} + +static void workspace_manager_handle_workspace_group( + void *data, zwlr_workspace_manager_v1 *_, zwlr_workspace_group_handle_v1 *workspace_group) { + static_cast(data)->handle_workspace_group_create(workspace_group); +} + +static void workspace_manager_handle_done(void *data, zwlr_workspace_manager_v1 *_) { + static_cast(data)->handle_done(); +} + +static void workspace_manager_handle_finished(void *data, zwlr_workspace_manager_v1 *_) { + static_cast(data)->handle_finished(); +} + +static const zwlr_workspace_manager_v1_listener workspace_manager_impl = { + .workspace_group = workspace_manager_handle_workspace_group, + .done = workspace_manager_handle_done, + .finished = workspace_manager_handle_finished, +}; + +zwlr_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name, + uint32_t version, void *data) { + auto *workspace_manager = static_cast( + wl_registry_bind(registry, name, &zwlr_workspace_manager_v1_interface, version)); + + if (workspace_manager) + zwlr_workspace_manager_v1_add_listener(workspace_manager, &workspace_manager_impl, data); + else + spdlog::error("Failed to register manager"); + + return workspace_manager; +} + +static void workspace_group_handle_output_enter(void *data, zwlr_workspace_group_handle_v1 *_, + wl_output *output) { + static_cast(data)->handle_output_enter(output); +} + +static void workspace_group_handle_output_leave(void *data, zwlr_workspace_group_handle_v1 *_, + wl_output *output) { + static_cast(data)->handle_output_leave(); +} + +static void workspace_group_handle_workspace(void *data, zwlr_workspace_group_handle_v1 *_, + zwlr_workspace_handle_v1 *workspace) { + static_cast(data)->handle_workspace_create(workspace); +} + +static void workspace_group_handle_remove(void *data, zwlr_workspace_group_handle_v1 *_) { + static_cast(data)->handle_remove(); +} + +static const zwlr_workspace_group_handle_v1_listener workspace_group_impl = { + .output_enter = workspace_group_handle_output_enter, + .output_leave = workspace_group_handle_output_leave, + .workspace = workspace_group_handle_workspace, + .remove = workspace_group_handle_remove}; + +void add_workspace_group_listener(zwlr_workspace_group_handle_v1 *workspace_group_handle, + void * data) { + zwlr_workspace_group_handle_v1_add_listener(workspace_group_handle, &workspace_group_impl, data); +} + +void workspace_handle_name(void *data, struct zwlr_workspace_handle_v1 *_, const char *name) { + static_cast(data)->handle_name(name); +} + +void workspace_handle_coordinates(void *data, struct zwlr_workspace_handle_v1 *_, + struct wl_array *coordinates) { + std::vector coords_vec; + auto coords = static_cast(coordinates->data); + for (size_t i = 0; i < coordinates->size; ++i) { + coords_vec.push_back(coords[i]); + } + + static_cast(data)->handle_coordinates(coords_vec); +} + +void workspace_handle_state(void *data, struct zwlr_workspace_handle_v1 *_, + struct wl_array *state) { + std::vector state_vec; + auto coords = static_cast(state->data); + for (size_t i = 0; i < state->size; ++i) { + state_vec.push_back(coords[i]); + } + + static_cast(data)->handle_state(state_vec); +} + +void workspace_handle_remove(void *data, struct zwlr_workspace_handle_v1 *_) { + static_cast(data)->handle_remove(); +} + +static const zwlr_workspace_handle_v1_listener workspace_impl = { + .name = workspace_handle_name, + .coordinates = workspace_handle_coordinates, + .state = workspace_handle_state, + .remove = workspace_handle_remove}; + +void add_workspace_listener(zwlr_workspace_handle_v1 *workspace_handle, void *data) { + zwlr_workspace_handle_v1_add_listener(workspace_handle, &workspace_impl, data); +} +} // namespace waybar::modules::wlr diff --git a/src/modules/wlr/workspaces.cpp b/src/modules/wlr/workspaces.cpp deleted file mode 100644 index 0d9ae0b..0000000 --- a/src/modules/wlr/workspaces.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "modules/wlr/workspaces.hpp" - -#include -#include - -#include - -namespace waybar::modules::wlr { - -static void handle_global(void *data, wl_registry *registry, uint32_t name, - const char *interface, uint32_t version) { - if (std::strcmp(interface, zwlr_workspace_manager_v1_interface.name) == 0) { - static_cast(data)->register_manager(registry, name, version); - } -} - -static void handle_global_remove(void *data, wl_registry *registry, uint32_t name) { - /* Nothing to do here */ -} - -static const wl_registry_listener registry_listener_impl = {.global = handle_global, - .global_remove = handle_global_remove}; - -WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar, - const Json::Value &config) - : waybar::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_); - - // wlr stuff - wl_display * display = Client::inst()->wl_display; - wl_registry *registry = wl_display_get_registry(display); - - wl_registry_add_listener(registry, ®istry_listener_impl, this); - wl_display_roundtrip(display); - - if (!workspace_manager_) { - return; - } -} - -static void workspace_manager_handle_workspace_group( - void *data, zwlr_workspace_manager_v1 *_, - zwlr_workspace_group_handle_v1 *workspace_group) { - static_cast(data)->handle_workspace_group_create(workspace_group); -} - -static void workspace_manager_handle_done(void *data, zwlr_workspace_manager_v1 *_) { - static_cast(data)->handle_done(); -} - -static void workspace_manager_handle_finished(void *data, zwlr_workspace_manager_v1 *_) { - static_cast(data)->handle_finished(); -} - -static const zwlr_workspace_manager_v1_listener workspace_manager_impl = { - .workspace_group = workspace_manager_handle_workspace_group, - .done = workspace_manager_handle_done, - .finished = workspace_manager_handle_finished, -}; - -auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) - -> void { - if (workspace_manager_) { - spdlog::warn("Register workspace manager again although already registered!"); - return; - } - if (version != 1) { - spdlog::warn("Using different workspace manager protocol version: {}", version); - } - - workspace_manager_ = static_cast( - wl_registry_bind(registry, name, &zwlr_workspace_manager_v1_interface, version)); - - if (workspace_manager_) - zwlr_workspace_manager_v1_add_listener(workspace_manager_, &workspace_manager_impl, this); - else - spdlog::debug("Failed to register manager"); -} -auto WorkspaceManager::handle_workspace_group_create( - zwlr_workspace_group_handle_v1 *workspace_group_handle) -> void { - groups_.push_back(std::make_unique(bar_, config_, *this, *workspace_group_handle)); -} - -auto WorkspaceManager::handle_finished() -> void { - zwlr_workspace_manager_v1_destroy(workspace_manager_); - workspace_manager_ = nullptr; -} -auto WorkspaceManager::handle_done() -> void {} -auto WorkspaceManager::update() -> void { - for (auto &group : groups_) { - group->update(); - } - AModule::update(); -} - -static void workspace_group_handle_output_enter(void *data, - zwlr_workspace_group_handle_v1 *_, - wl_output *output) { - static_cast(data)->handle_output_enter(*output); -} - -static void workspace_group_handle_output_leave(void *data, - zwlr_workspace_group_handle_v1 *_, - wl_output *output) { - static_cast(data)->handle_output_leave(*output); -} - -static void workspace_group_handle_workspace(void *data, - zwlr_workspace_group_handle_v1 *_, - zwlr_workspace_handle_v1 *workspace) { - static_cast(data)->handle_workspace_create(*workspace); -} - -static void workspace_group_handle_remove(void *data, - zwlr_workspace_group_handle_v1 *_) { - static_cast(data)->handle_remove(); -} - -static const zwlr_workspace_group_handle_v1_listener workspace_group_impl = { - .output_enter = workspace_group_handle_output_enter, - .output_leave = workspace_group_handle_output_leave, - .workspace = workspace_group_handle_workspace, - .remove = workspace_group_handle_remove -}; - -WorkspaceGroup::WorkspaceGroup(const Bar & bar, const Json::Value &config, WorkspaceManager &manager, - zwlr_workspace_group_handle_v1 &workspace_group_handle) - : bar_(bar), config_(config), workspace_manager_(manager), workspace_group_handle_(workspace_group_handle) -{ - zwlr_workspace_group_handle_v1_add_listener(&workspace_group_handle, &workspace_group_impl, this); -} -auto WorkspaceGroup::handle_workspace_create(zwlr_workspace_handle_v1 &workspace) -> void { - workspaces_.push_back(std::make_unique(bar_, config_, *this, workspace)); -} -auto WorkspaceGroup::handle_remove() -> void {} -auto WorkspaceGroup::handle_output_enter(wl_output &output) -> void {} -auto WorkspaceGroup::handle_output_leave(wl_output &output) -> void {} -auto WorkspaceGroup::update() -> void { - -} -Workspace::Workspace(const Bar &bar, const Json::Value &config, - WorkspaceGroup &workspace_group, zwlr_workspace_handle_v1 &workspace) - : bar_(bar), config_(config), workspace_group_(workspace_group), workspace_handle_(workspace) -{ -} -} // namespace waybar::modules::wlr \ No newline at end of file From 99f3e37ccf6f5e37813b34732c3a1fbcc98ae249 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 5 Aug 2020 23:00:36 +0200 Subject: [PATCH 039/355] chore: update archlinux, debian dockerfiles --- Dockerfiles/archlinux | 2 +- Dockerfiles/debian | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index d8ae16f..0b618ff 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,4 +3,4 @@ FROM archlinux/base:latest RUN pacman -Syu --noconfirm && \ - pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient --noconfirm + pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 077aca8..9f65dbf 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection apt-get install libgirepository1.0-dev && \ apt-get clean From 2ca20f90505a5fb46ff882b89737eaed7dd2122a Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 5 Aug 2020 23:01:37 +0200 Subject: [PATCH 040/355] chore: remove unwanted typo --- Dockerfiles/debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 9f65dbf..cee1744 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection apt-get install libgirepository1.0-dev && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev && \ apt-get clean From 01c682c41ecb6a415b477745166ef430a344c370 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 5 Aug 2020 23:26:59 +0200 Subject: [PATCH 041/355] chore: v0.9.3 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index dd56c29..56b45be 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.2', + version: '0.9.3', license: 'MIT', default_options : [ 'cpp_std=c++17', From 7638f5c5958d0f9243c8b71569b9cb048f439a7a Mon Sep 17 00:00:00 2001 From: dmitry Date: Thu, 6 Aug 2020 01:42:57 +0300 Subject: [PATCH 042/355] Add base name representation --- include/modules/wlr/workspace_manager.hpp | 14 ++++++++---- src/modules/wlr/workspace_manager.cpp | 26 ++++++++++++++++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index 3c8fe99..f606331 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include #include #include @@ -23,6 +25,7 @@ class Workspace { auto update() -> void; auto id() const -> uint32_t { return id_; } + // wlr stuff auto handle_name(const std::string &name) -> void { name_ = name; } auto handle_coordinates(const std::vector &coordinates) -> void { coordinates_ = coordinates; @@ -46,8 +49,9 @@ class Workspace { std::string name_; std::vector coordinates_; - const Gtk::Box box_; - const Gtk::Image icon_; + Gtk::Button button_; + Gtk::Box content_; + Gtk::Label label_; }; class WorkspaceGroup { @@ -62,11 +66,12 @@ class WorkspaceGroup { // wlr stuff auto handle_workspace_create(zwlr_workspace_handle_v1 *workspace_handle) -> void; - auto handle_remove() -> void; auto handle_output_enter(wl_output *output) -> void; auto handle_output_leave() -> void; + auto add_button(Gtk::Button &button) -> void; + private: static uint32_t group_global_id; const waybar::Bar &bar_; @@ -93,10 +98,11 @@ class WorkspaceManager : public AModule { auto register_manager(wl_registry *registry, uint32_t name, uint32_t version) -> void; auto handle_workspace_group_create(zwlr_workspace_group_handle_v1 *workspace_group_handle) -> void; - auto handle_done() -> void; auto handle_finished() -> void; + auto add_button(Gtk::Button &button) -> void { box_.pack_start(button, false, false); } + private: const waybar::Bar & bar_; Gtk::Box box_; diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 04a3188..25ce525 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -14,7 +14,7 @@ uint32_t WorkspaceGroup::group_global_id = 0; WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config) - : waybar::AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), + : waybar::AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { box_.set_name("workspaces"); @@ -53,6 +53,7 @@ auto WorkspaceManager::handle_finished() -> void { } auto WorkspaceManager::handle_done() -> void { + dp.emit(); } auto WorkspaceManager::update() -> void { @@ -92,8 +93,11 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, const Json::Value &config, Worksp id_(++group_global_id) { add_workspace_group_listener(workspace_group_handle, this); } +auto WorkspaceGroup::add_button(Gtk::Button &button) -> void { + workspace_manager_.add_button(button); +} -WorkspaceGroup::~WorkspaceGroup()->void { +WorkspaceGroup::~WorkspaceGroup() { if (!workspace_group_handle_) { return; } @@ -135,7 +139,7 @@ auto WorkspaceGroup::remove_workspace(uint32_t id) -> void { [id](const std::unique_ptr &w) { return w->id() == id; }); if (it == workspaces_.end()) { - spdlog::warn("Can't find group with id {}", id); + spdlog::warn("Can't find workspace with id {}", id); return; } @@ -148,8 +152,17 @@ Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup & config_(config), workspace_group_(workspace_group), workspace_handle_(workspace), - id_(++workspace_global_id) { + id_(++workspace_global_id), + content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} { add_workspace_listener(workspace, this); + workspace_group.add_button(button_); + button_.set_relief(Gtk::RELIEF_NONE); + label_.set_label(fmt::format("{name}", fmt::arg("name", "1"))); + label_.show(); + content_.add(label_); + content_.show(); + button_.add(content_); + button_.show(); } Workspace::~Workspace() { @@ -161,7 +174,10 @@ Workspace::~Workspace() { workspace_handle_ = nullptr; } -auto Workspace::update() -> void {} +auto Workspace::update() -> void { + label_.set_label(fmt::format("{name}", fmt::arg("name", name_))); + label_.show(); +} auto Workspace::handle_state(const std::vector &state) -> void { state_ = 0; From 6a2d214b55cb1e8517afccb18c3799b22e16a57e Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Wed, 5 Aug 2020 20:31:57 -0400 Subject: [PATCH 043/355] Fix titles containing & and other HTML entities --- src/modules/wlr/taskbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 23a9166..025e5c1 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -306,7 +306,7 @@ std::string Task::state_string(bool shortened) const void Task::handle_title(const char *title) { - title_ = title; + title_ = Glib::Markup::escape_text(title); } void Task::handle_app_id(const char *app_id) From 4cd31cf3c36db71c04eabf3dad1f12b877991015 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Wed, 5 Aug 2020 20:35:41 -0400 Subject: [PATCH 044/355] Fix wlr/taskbar all-outputs config string --- src/modules/wlr/taskbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 025e5c1..f7e11df 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -709,7 +709,7 @@ bool Taskbar::show_output(struct wl_output *output) const bool Taskbar::all_outputs() const { - static bool result = config_["all_outputs"].isBool() ? config_["all_outputs"].asBool() : false; + static bool result = config_["all-outputs"].isBool() ? config_["all-outputs"].asBool() : false; return result; } From 9ebfc54eb5cb52acc6b54aeaec86be196388061f Mon Sep 17 00:00:00 2001 From: NotAFile Date: Thu, 6 Aug 2020 16:04:30 +0200 Subject: [PATCH 045/355] switch workspace on mouse-down to match swaybar fixes #686 --- src/modules/sway/workspaces.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index fc6d5eb..7774351 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -249,7 +249,7 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { auto &&button = pair.first->second; box_.pack_start(button, false, false, 0); button.set_relief(Gtk::RELIEF_NONE); - button.signal_clicked().connect([this, node] { + button.signal_pressed().connect([this, node] { try { if (node["target_output"].isString()) { ipc_.sendCmd( From a446cd692d19acc215aa1fe42a2e24f7507ea2d5 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 6 Aug 2020 21:57:02 +0200 Subject: [PATCH 046/355] Fix MPD, add missing while loop --- src/modules/mpd.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index 957b3c7..d2877f3 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -63,20 +63,22 @@ auto waybar::modules::MPD::update() -> void { std::thread waybar::modules::MPD::event_listener() { return std::thread([this] { - 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) { - if (strcmp(e.what(), "Connection to MPD closed") == 0) { - spdlog::debug("{}: {}", module_name_, e.what()); - } else { - spdlog::warn("{}: {}", module_name_, e.what()); + 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) { + if (strcmp(e.what(), "Connection to MPD closed") == 0) { + spdlog::debug("{}: {}", module_name_, e.what()); + } else { + spdlog::warn("{}: {}", module_name_, e.what()); + } } } }); From d51adfe7bc2e6e653c1075b64f7b1a27c8ca6482 Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Thu, 6 Aug 2020 23:21:53 +0200 Subject: [PATCH 047/355] systemd: use standard targets, update service type --- resources/waybar.service.in | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/resources/waybar.service.in b/resources/waybar.service.in index 03262a3..2c907e9 100644 --- a/resources/waybar.service.in +++ b/resources/waybar.service.in @@ -1,13 +1,11 @@ [Unit] Description=Highly customizable Wayland bar for Sway and Wlroots based compositors. Documentation=https://github.com/Alexays/Waybar/wiki/ -PartOf=wayland-session.target -After=wayland-session.target +PartOf=graphical-session.target +After=graphical-session.target [Service] -Type=dbus -BusName=fr.arouillard.waybar ExecStart=@prefix@/bin/waybar [Install] -WantedBy=wayland-session.target +WantedBy=graphical-session.target From 4c251578e923760372bf51f2d9540d7abd9b204b Mon Sep 17 00:00:00 2001 From: dmitry Date: Fri, 7 Aug 2020 02:45:08 +0300 Subject: [PATCH 048/355] Add formatting and states handling --- include/modules/wlr/workspace_manager.hpp | 20 +++++- src/modules/wlr/workspace_manager.cpp | 80 ++++++++++++++++++++--- 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index f606331..8cd065f 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -25,6 +26,7 @@ class Workspace { auto update() -> void; auto id() const -> uint32_t { return id_; } + auto is_active() const -> bool { return state_ & static_cast(State::ACTIVE); } // wlr stuff auto handle_name(const std::string &name) -> void { name_ = name; } auto handle_coordinates(const std::vector &coordinates) -> void { @@ -33,9 +35,14 @@ class Workspace { auto handle_state(const std::vector &state) -> void; auto handle_remove() -> void; + auto handle_done() -> void; + auto handle_clicked() -> void; + enum class State { ACTIVE = 1 << 0 }; private: + auto get_icon() -> std::string; + static uint32_t workspace_global_id; const Bar & bar_; const Json::Value &config_; @@ -45,9 +52,12 @@ class Workspace { zwlr_workspace_handle_v1 *workspace_handle_; uint32_t state_ = 0; - uint32_t id_; - std::string name_; - std::vector coordinates_; + uint32_t id_; + std::string name_; + std::vector coordinates_; + static std::map icons_map_; + std::string format_; + bool with_icon_ = false; Gtk::Button button_; Gtk::Box content_; @@ -62,6 +72,7 @@ class WorkspaceGroup { auto update() -> void; auto id() const -> uint32_t { return id_; } + auto is_visible() const -> bool { return output_ != nullptr; } auto remove_workspace(uint32_t id_) -> void; // wlr stuff @@ -71,6 +82,8 @@ class WorkspaceGroup { auto handle_output_leave() -> void; auto add_button(Gtk::Button &button) -> void; + auto handle_done() -> void; + auto commit() -> void; private: static uint32_t group_global_id; @@ -102,6 +115,7 @@ class WorkspaceManager : public AModule { auto handle_finished() -> void; auto add_button(Gtk::Button &button) -> void { box_.pack_start(button, false, false); } + auto commit() -> void; private: const waybar::Bar & bar_; diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 25ce525..cf0510d 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -9,8 +9,9 @@ namespace waybar::modules::wlr { -uint32_t Workspace::workspace_global_id = 0; -uint32_t WorkspaceGroup::group_global_id = 0; +uint32_t Workspace::workspace_global_id = 0; +uint32_t WorkspaceGroup::group_global_id = 0; +std::map Workspace::icons_map_; WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config) @@ -53,6 +54,9 @@ auto WorkspaceManager::handle_finished() -> void { } auto WorkspaceManager::handle_done() -> void { + for (auto &group : groups_) { + group->handle_done(); + } dp.emit(); } @@ -83,6 +87,7 @@ auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void { groups_.erase(it); } +auto WorkspaceManager::commit() -> void { zwlr_workspace_manager_v1_commit(workspace_manager_); } WorkspaceGroup::WorkspaceGroup(const Bar &bar, const Json::Value &config, WorkspaceManager &manager, zwlr_workspace_group_handle_v1 *workspace_group_handle) @@ -145,6 +150,14 @@ auto WorkspaceGroup::remove_workspace(uint32_t id) -> void { workspaces_.erase(it); } +auto WorkspaceGroup::handle_done() -> void { + if (is_visible()) { + for (auto &workspace : workspaces_) { + workspace->handle_done(); + } + } +} +auto WorkspaceGroup::commit() -> void { workspace_manager_.commit(); } Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, zwlr_workspace_handle_v1 *workspace) @@ -152,17 +165,30 @@ Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup & config_(config), workspace_group_(workspace_group), workspace_handle_(workspace), - id_(++workspace_global_id), - content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} { + id_(++workspace_global_id) { add_workspace_listener(workspace, this); + + auto config_format = config["format"]; + + format_ = config_format.isString() ? config_format.asString() : "{name}"; + with_icon_ = format_.find("{icon}") != std::string::npos; + + if (with_icon_ && icons_map_.empty()) { + auto format_icons = config["format-icons"]; + for (auto &name : format_icons.getMemberNames()) { + icons_map_.emplace(name, format_icons[name].asString()); + } + } + + button_.signal_clicked().connect(sigc::mem_fun(this, &Workspace::handle_clicked)); + workspace_group.add_button(button_); button_.set_relief(Gtk::RELIEF_NONE); - label_.set_label(fmt::format("{name}", fmt::arg("name", "1"))); - label_.show(); - content_.add(label_); - content_.show(); + content_.set_center_widget(label_); button_.add(content_); button_.show(); + label_.show(); + content_.show(); } Workspace::~Workspace() { @@ -175,8 +201,8 @@ Workspace::~Workspace() { } auto Workspace::update() -> void { - label_.set_label(fmt::format("{name}", fmt::arg("name", name_))); - label_.show(); + label_.set_markup(fmt::format( + format_, fmt::arg("name", name_), fmt::arg("icon", with_icon_ ? get_icon() : ""))); } auto Workspace::handle_state(const std::vector &state) -> void { @@ -195,4 +221,38 @@ auto Workspace::handle_remove() -> void { workspace_handle_ = nullptr; workspace_group_.remove_workspace(id_); } +auto Workspace::handle_done() -> void { + spdlog::debug("Workspace {} changed to state {}", id_, state_); + auto style_context = button_.get_style_context(); + if (is_active()) { + style_context->add_class("focused"); + } else { + style_context->remove_class("focused"); + } +} +auto Workspace::get_icon() -> std::string { + if (is_active()) { + auto focused_icon_it = icons_map_.find("focused"); + if (focused_icon_it != icons_map_.end()) { + return focused_icon_it->second; + } + } + + auto named_icon_it = icons_map_.find(name_); + if (named_icon_it != icons_map_.end()) { + return named_icon_it->second; + } + + auto default_icon_it = icons_map_.find("default"); + if (default_icon_it != icons_map_.end()) { + return default_icon_it->second; + } + + return name_; +} +auto Workspace::handle_clicked() -> void { + spdlog::debug("Workspace {} clicked", (void*)workspace_handle_); + zwlr_workspace_handle_v1_activate(workspace_handle_); + workspace_group_.commit(); +} } // namespace waybar::modules::wlr \ No newline at end of file From 0ad29a5960c945fed328d300fce3d897d2a68994 Mon Sep 17 00:00:00 2001 From: dmitry Date: Fri, 7 Aug 2020 23:46:47 +0300 Subject: [PATCH 049/355] Finish base implementation --- include/modules/wlr/workspace_manager.hpp | 28 +++--- src/modules/wlr/workspace_manager.cpp | 86 +++++++++++++++---- src/modules/wlr/workspace_manager_binding.cpp | 12 ++- 3 files changed, 96 insertions(+), 30 deletions(-) diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index 8cd065f..abec5d6 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -21,29 +21,31 @@ class WorkspaceGroup; class Workspace { public: Workspace(const waybar::Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, - zwlr_workspace_handle_v1 *workspace); + zwlr_workspace_handle_v1 *workspace, uint32_t id); ~Workspace(); auto update() -> void; auto id() const -> uint32_t { return id_; } auto is_active() const -> bool { return state_ & static_cast(State::ACTIVE); } // wlr stuff - auto handle_name(const std::string &name) -> void { name_ = name; } - auto handle_coordinates(const std::vector &coordinates) -> void { - coordinates_ = coordinates; - } + auto handle_name(const std::string &name) -> void; + auto handle_coordinates(const std::vector &coordinates) -> void; auto handle_state(const std::vector &state) -> void; auto handle_remove() -> void; auto handle_done() -> void; auto handle_clicked() -> void; + auto show() -> void { button_.show(); } + auto hide() -> void { button_.hide(); } + auto get_button_ref() -> Gtk::Button & { return button_; } + auto get_name() -> std::string & { return name_; } + auto get_coords() -> std::vector & { return coordinates_; } enum class State { ACTIVE = 1 << 0 }; private: auto get_icon() -> std::string; - static uint32_t workspace_global_id; const Bar & bar_; const Json::Value &config_; WorkspaceGroup & workspace_group_; @@ -66,8 +68,9 @@ class Workspace { class WorkspaceGroup { public: - WorkspaceGroup(const waybar::Bar &bar, const Json::Value &config, WorkspaceManager &manager, - zwlr_workspace_group_handle_v1 *workspace_group_handle); + WorkspaceGroup(const waybar::Bar &bar, Gtk::Box &box, const Json::Value &config, + WorkspaceManager &manager, zwlr_workspace_group_handle_v1 *workspace_group_handle, + uint32_t id); ~WorkspaceGroup(); auto update() -> void; @@ -84,10 +87,12 @@ class WorkspaceGroup { auto add_button(Gtk::Button &button) -> void; auto handle_done() -> void; auto commit() -> void; + auto sort_workspaces() -> void; private: - static uint32_t group_global_id; + static uint32_t workspace_global_id; const waybar::Bar &bar_; + Gtk::Box & box_; const Json::Value &config_; WorkspaceManager & workspace_manager_; @@ -97,6 +102,8 @@ class WorkspaceGroup { uint32_t id_; std::vector> workspaces_; + bool sort_by_name = true; + bool sort_by_coordinates = true; }; class WorkspaceManager : public AModule { @@ -114,7 +121,6 @@ class WorkspaceManager : public AModule { auto handle_done() -> void; auto handle_finished() -> void; - auto add_button(Gtk::Button &button) -> void { box_.pack_start(button, false, false); } auto commit() -> void; private: @@ -124,6 +130,8 @@ class WorkspaceManager : public AModule { // wlr stuff zwlr_workspace_manager_v1 *workspace_manager_ = nullptr; + + static uint32_t group_global_id; }; } // namespace waybar::modules::wlr diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index cf0510d..2892d55 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -9,8 +9,8 @@ namespace waybar::modules::wlr { -uint32_t Workspace::workspace_global_id = 0; -uint32_t WorkspaceGroup::group_global_id = 0; +uint32_t WorkspaceGroup::workspace_global_id = 0; +uint32_t WorkspaceManager::group_global_id = 0; std::map Workspace::icons_map_; WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar, @@ -44,8 +44,10 @@ auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, ui auto WorkspaceManager::handle_workspace_group_create( zwlr_workspace_group_handle_v1 *workspace_group_handle) -> void { - groups_.push_back(std::make_unique(bar_, config_, *this, workspace_group_handle)); - spdlog::debug("Workspace group {} created", groups_.back()->id()); + auto new_id = ++group_global_id; + groups_.push_back( + std::make_unique(bar_, box_, config_, *this, workspace_group_handle, new_id)); + spdlog::debug("Workspace group {} created", new_id); } auto WorkspaceManager::handle_finished() -> void { @@ -89,17 +91,28 @@ auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void { } auto WorkspaceManager::commit() -> void { zwlr_workspace_manager_v1_commit(workspace_manager_); } -WorkspaceGroup::WorkspaceGroup(const Bar &bar, const Json::Value &config, WorkspaceManager &manager, - zwlr_workspace_group_handle_v1 *workspace_group_handle) +WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value &config, + WorkspaceManager & manager, + zwlr_workspace_group_handle_v1 *workspace_group_handle, uint32_t id) : bar_(bar), + box_(box), config_(config), workspace_manager_(manager), workspace_group_handle_(workspace_group_handle), - id_(++group_global_id) { + id_(id) { add_workspace_group_listener(workspace_group_handle, this); + auto config_sort_by_name = config_["sort_by_name"]; + if (config_sort_by_name.isBool()) { + sort_by_name = config_sort_by_name.asBool(); + } + + auto config_sort_by_coordinates = config_["sort_by_coordinates"]; + if (config_sort_by_coordinates.isBool()) { + sort_by_coordinates = config_sort_by_coordinates.asBool(); + } } auto WorkspaceGroup::add_button(Gtk::Button &button) -> void { - workspace_manager_.add_button(button); + box_.pack_start(button, false, false); } WorkspaceGroup::~WorkspaceGroup() { @@ -112,8 +125,9 @@ WorkspaceGroup::~WorkspaceGroup() { } auto WorkspaceGroup::handle_workspace_create(zwlr_workspace_handle_v1 *workspace) -> void { - workspaces_.push_back(std::make_unique(bar_, config_, *this, workspace)); - spdlog::debug("Workspace {} created", workspaces_.back()->id()); + auto new_id = ++workspace_global_id; + workspaces_.push_back(std::make_unique(bar_, config_, *this, workspace, new_id)); + spdlog::debug("Workspace {} created", new_id); } auto WorkspaceGroup::handle_remove() -> void { @@ -124,12 +138,18 @@ auto WorkspaceGroup::handle_remove() -> void { auto WorkspaceGroup::handle_output_enter(wl_output *output) -> void { spdlog::debug("Output {} assigned to {} group", (void *)output, id_); + for (auto &workspace : workspaces_) { + workspace->show(); + } output_ = output; } auto WorkspaceGroup::handle_output_leave() -> void { spdlog::debug("Output {} remove from {} group", (void *)output_, id_); output_ = nullptr; + for (auto &workspace : workspaces_) { + workspace->hide(); + } } auto WorkspaceGroup::update() -> void { @@ -150,22 +170,37 @@ auto WorkspaceGroup::remove_workspace(uint32_t id) -> void { workspaces_.erase(it); } + auto WorkspaceGroup::handle_done() -> void { - if (is_visible()) { - for (auto &workspace : workspaces_) { - workspace->handle_done(); - } + for (auto &workspace : workspaces_) { + workspace->handle_done(); } } auto WorkspaceGroup::commit() -> void { workspace_manager_.commit(); } +auto WorkspaceGroup::sort_workspaces() -> void { + auto cmp = [=](std::unique_ptr &lhs, std::unique_ptr &rhs) { + if (sort_by_name && lhs->get_name() != rhs->get_name()) { + return lhs->get_name() < rhs->get_name(); + } + + return lhs->get_coords() < rhs->get_coords(); + }; + std::sort(workspaces_.begin(), workspaces_.end(), cmp); + for (size_t i = 0; i < workspaces_.size(); ++i) { + for (auto &workspace : workspaces_) { + box_.reorder_child(workspace->get_button_ref(), i); + } + } +} + Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, - zwlr_workspace_handle_v1 *workspace) + zwlr_workspace_handle_v1 *workspace, uint32_t id) : bar_(bar), config_(config), workspace_group_(workspace_group), workspace_handle_(workspace), - id_(++workspace_global_id) { + id_(id) { add_workspace_listener(workspace, this); auto config_format = config["format"]; @@ -186,6 +221,10 @@ Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup & button_.set_relief(Gtk::RELIEF_NONE); content_.set_center_widget(label_); button_.add(content_); + if (!workspace_group.is_visible()) { + return; + } + button_.show(); label_.show(); content_.show(); @@ -221,6 +260,7 @@ auto Workspace::handle_remove() -> void { workspace_handle_ = nullptr; workspace_group_.remove_workspace(id_); } + auto Workspace::handle_done() -> void { spdlog::debug("Workspace {} changed to state {}", id_, state_); auto style_context = button_.get_style_context(); @@ -230,6 +270,7 @@ auto Workspace::handle_done() -> void { style_context->remove_class("focused"); } } + auto Workspace::get_icon() -> std::string { if (is_active()) { auto focused_icon_it = icons_map_.find("focused"); @@ -250,9 +291,20 @@ auto Workspace::get_icon() -> std::string { return name_; } + auto Workspace::handle_clicked() -> void { - spdlog::debug("Workspace {} clicked", (void*)workspace_handle_); + spdlog::debug("Workspace {} clicked", (void *)workspace_handle_); zwlr_workspace_handle_v1_activate(workspace_handle_); workspace_group_.commit(); } + +auto Workspace::handle_name(const std::string &name) -> void { + name_ = name; + workspace_group_.sort_workspaces(); +} + +auto Workspace::handle_coordinates(const std::vector &coordinates) -> void { + coordinates_ = coordinates; + workspace_group_.sort_workspaces(); +} } // namespace waybar::modules::wlr \ No newline at end of file diff --git a/src/modules/wlr/workspace_manager_binding.cpp b/src/modules/wlr/workspace_manager_binding.cpp index f6db711..c7d84bb 100644 --- a/src/modules/wlr/workspace_manager_binding.cpp +++ b/src/modules/wlr/workspace_manager_binding.cpp @@ -107,12 +107,18 @@ void workspace_handle_coordinates(void *data, struct zwlr_workspace_handle_v1 *_ static_cast(data)->handle_coordinates(coords_vec); } -void workspace_handle_state(void *data, struct zwlr_workspace_handle_v1 *_, +void workspace_handle_state(void *data, struct zwlr_workspace_handle_v1 *workspace_handle, struct wl_array *state) { std::vector state_vec; - auto coords = static_cast(state->data); + auto states = static_cast(state->data); for (size_t i = 0; i < state->size; ++i) { - state_vec.push_back(coords[i]); + // To sync server and pending states + if (states[i] == ZWLR_WORKSPACE_HANDLE_V1_STATE_ACTIVE) { + zwlr_workspace_handle_v1_activate(workspace_handle); + } else { + zwlr_workspace_handle_v1_deactivate(workspace_handle); + } + state_vec.push_back(states[i]); } static_cast(data)->handle_state(state_vec); From 42b6c089f34b24cb46a7add4892112a4b15799ca Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 8 Aug 2020 00:09:35 +0300 Subject: [PATCH 050/355] Add docs and adjust sorting --- man/waybar-wlr-workspaces.5.scd | 69 +++++++++++++++++++++++++++ man/waybar.5.scd | 1 + meson.build | 1 + src/modules/wlr/workspace_manager.cpp | 23 +++++++-- 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 man/waybar-wlr-workspaces.5.scd diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-wlr-workspaces.5.scd new file mode 100644 index 0000000..732c01b --- /dev/null +++ b/man/waybar-wlr-workspaces.5.scd @@ -0,0 +1,69 @@ +waybar-wlr-workspaces(5) + +# NAME + +waybar - wlr workspaces module + +# DESCRIPTION + +The *workspaces* module displays the currently used workspaces in waynland compositor. + +# CONFIGURATION + +Addressed by *wlr/workspaces* + +*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*. + +*sort-by-name*: ++ + typeof: bool ++ + default: true ++ + Should workspaces be sorted by name. + +*sort-by-coordinates*: ++ + typeof: bool ++ + default: false ++ + Should workspaces be sorted by coordinates. + Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first. + If both are false - sort by id will be performed. + +# FORMAT REPLACEMENTS + +*{name}*: Number stripped from workspace value. + +*{icon}*: Icon, as defined in *format-icons*. + +# ICONS + +Additional to workspace name matching, the following *format-icons* can be set. + +- *default*: Will be shown, when no string matches is found. +- *focused*: Will be shown, when workspace is focused + +# EXAMPLES + +``` +"wlr/workspaces": { + "format": "{name}: {icon}", + "format-icons": { + "1": "", + "2": "", + "3": "", + "4": "", + "5": "", + "focused": "", + "default": "" + } +} +``` + +# Style + +- *#workspaces button* +- *#workspaces button.focused* diff --git a/man/waybar.5.scd b/man/waybar.5.scd index 5267110..a758821 100644 --- a/man/waybar.5.scd +++ b/man/waybar.5.scd @@ -197,5 +197,6 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270. - *waybar-sway-window(5)* - *waybar-sway-workspaces(5)* - *waybar-wlr-taskbar(5)* +- *waybar-wlr-workspaces(5)* - *waybar-temperature(5)* - *waybar-tray(5)* diff --git a/meson.build b/meson.build index 638a766..19c6b4a 100644 --- a/meson.build +++ b/meson.build @@ -272,6 +272,7 @@ if scdoc.found() 'waybar-tray.5.scd', 'waybar-states.5.scd', 'waybar-wlr-taskbar.5.scd', + 'waybar-wlr-workspaces.5.scd', 'waybar-bluetooth.5.scd', ] diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 2892d55..d86dc62 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -101,12 +101,12 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value workspace_group_handle_(workspace_group_handle), id_(id) { add_workspace_group_listener(workspace_group_handle, this); - auto config_sort_by_name = config_["sort_by_name"]; + auto config_sort_by_name = config_["sort-by-name"]; if (config_sort_by_name.isBool()) { sort_by_name = config_sort_by_name.asBool(); } - auto config_sort_by_coordinates = config_["sort_by_coordinates"]; + auto config_sort_by_coordinates = config_["sort-by-coordinates"]; if (config_sort_by_coordinates.isBool()) { sort_by_coordinates = config_sort_by_coordinates.asBool(); } @@ -180,12 +180,25 @@ auto WorkspaceGroup::commit() -> void { workspace_manager_.commit(); } auto WorkspaceGroup::sort_workspaces() -> void { auto cmp = [=](std::unique_ptr &lhs, std::unique_ptr &rhs) { - if (sort_by_name && lhs->get_name() != rhs->get_name()) { - return lhs->get_name() < rhs->get_name(); + auto is_name_less = lhs->get_name() < rhs->get_name(); + auto is_name_eq = lhs->get_name() == rhs->get_name(); + auto is_coords_less = lhs->get_coords() < rhs->get_coords(); + if (sort_by_name) { + if (sort_by_coordinates) { + return is_name_eq ? is_coords_less : is_name_less; + } + else { + return is_name_less; + } } - return lhs->get_coords() < rhs->get_coords(); + if (sort_by_coordinates) { + return is_coords_less; + } + + return lhs->id() < rhs->id(); }; + std::sort(workspaces_.begin(), workspaces_.end(), cmp); for (size_t i = 0; i < workspaces_.size(); ++i) { for (auto &workspace : workspaces_) { From 50e8f7ca86f871cb4a18828a811bcbde36864473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Schl=C3=B6mer?= Date: Sat, 8 Aug 2020 13:17:56 +0200 Subject: [PATCH 051/355] add repo info to README --- README.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f7bc4c8..74f4660 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [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** +#### Current features - Sway (Workspaces, Binding mode, Focused window name) - Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Local time @@ -22,11 +22,21 @@ - Multiple output configuration - And much more customizations -**Configuration and Styling** +#### Configuration and Styling [See the wiki for more details](https://github.com/Alexays/Waybar/wiki). -**How to build** +### Installation + +Waybar is available from a number of Linux distributions: + +[![Packaging status](https://repology.org/badge/vertical-allrepos/waybar.svg)](https://repology.org/project/waybar/versions) + +An Ubuntu PPA with more recent versions is available +[here](https://launchpad.net/~nschloe/+archive/ubuntu/waybar). + + +#### Building from source ```bash $ git clone https://github.com/Alexays/Waybar From 9b41b9593418772ce578a87de5984d4e37ef7f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorben=20G=C3=BCnther?= Date: Mon, 10 Aug 2020 20:53:29 +0200 Subject: [PATCH 052/355] Fix crash with fmt --- include/util/format.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/util/format.hpp b/include/util/format.hpp index 0147701..288d8f0 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -23,7 +23,7 @@ namespace fmt { constexpr auto parse(ParseContext& ctx) -> decltype (ctx.begin()) { auto it = ctx.begin(), end = ctx.end(); if (it != end && *it == ':') ++it; - if (*it == '>' || *it == '<' || *it == '=') { + if (it && (*it == '>' || *it == '<' || *it == '=')) { spec = *it; ++it; } From 22409d27c5a6307880828841a8b7c4a113b3dc1b Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 11 Aug 2020 00:28:56 +0300 Subject: [PATCH 053/355] Fix docs typos Add removing buttons Adjust handling multiple outputs. --- include/modules/wlr/workspace_manager.hpp | 1 + man/waybar-wlr-workspaces.5.scd | 7 ++++--- src/modules/wlr/workspace_manager.cpp | 21 ++++++++++++++++++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index abec5d6..73c5893 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -85,6 +85,7 @@ class WorkspaceGroup { auto handle_output_leave() -> void; auto add_button(Gtk::Button &button) -> void; + auto remove_button(Gtk::Button &button) -> void; auto handle_done() -> void; auto commit() -> void; auto sort_workspaces() -> void; diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-wlr-workspaces.5.scd index 732c01b..c1e827f 100644 --- a/man/waybar-wlr-workspaces.5.scd +++ b/man/waybar-wlr-workspaces.5.scd @@ -6,7 +6,7 @@ waybar - wlr workspaces module # DESCRIPTION -The *workspaces* module displays the currently used workspaces in waynland compositor. +The *workspaces* module displays the currently used workspaces in wayland compositor. # CONFIGURATION @@ -35,7 +35,7 @@ Addressed by *wlr/workspaces* # FORMAT REPLACEMENTS -*{name}*: Number stripped from workspace value. +*{name}*: Name of workspace assigned by compositor *{icon}*: Icon, as defined in *format-icons*. @@ -43,7 +43,7 @@ Addressed by *wlr/workspaces* Additional to workspace name matching, the following *format-icons* can be set. -- *default*: Will be shown, when no string matches is found. +- *default*: Will be shown, when no string match is found. - *focused*: Will be shown, when workspace is focused # EXAMPLES @@ -65,5 +65,6 @@ Additional to workspace name matching, the following *format-icons* can be set. # Style +- *#workspaces* - *#workspaces button* - *#workspaces button.focused* diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index d86dc62..20294f4 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -1,5 +1,6 @@ #include "modules/wlr/workspace_manager.hpp" +#include #include #include @@ -77,6 +78,7 @@ WorkspaceManager::~WorkspaceManager() { zwlr_workspace_manager_v1_destroy(workspace_manager_); workspace_manager_ = nullptr; } + auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void { auto it = std::find_if(groups_.begin(), groups_.end(), @@ -111,6 +113,7 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value sort_by_coordinates = config_sort_by_coordinates.asBool(); } } + auto WorkspaceGroup::add_button(Gtk::Button &button) -> void { box_.pack_start(button, false, false); } @@ -138,15 +141,25 @@ auto WorkspaceGroup::handle_remove() -> void { auto WorkspaceGroup::handle_output_enter(wl_output *output) -> void { spdlog::debug("Output {} assigned to {} group", (void *)output, id_); + output_ = output; + + if (output != gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj())) { + return; + } + for (auto &workspace : workspaces_) { workspace->show(); } - output_ = output; } auto WorkspaceGroup::handle_output_leave() -> void { spdlog::debug("Output {} remove from {} group", (void *)output_, id_); output_ = nullptr; + + if (output_ != gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj())) { + return; + } + for (auto &workspace : workspaces_) { workspace->hide(); } @@ -176,6 +189,7 @@ auto WorkspaceGroup::handle_done() -> void { workspace->handle_done(); } } + auto WorkspaceGroup::commit() -> void { workspace_manager_.commit(); } auto WorkspaceGroup::sort_workspaces() -> void { @@ -207,6 +221,10 @@ auto WorkspaceGroup::sort_workspaces() -> void { } } +auto WorkspaceGroup::remove_button(Gtk::Button &button) -> void { + box_.remove(button); +} + Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, zwlr_workspace_handle_v1 *workspace, uint32_t id) : bar_(bar), @@ -244,6 +262,7 @@ Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup & } Workspace::~Workspace() { + workspace_group_.remove_button(button_); if (!workspace_handle_) { return; } From 8f10c9056c901f9a89b03ce4fbe2f8d108cb49d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Wed, 12 Aug 2020 11:38:48 +0100 Subject: [PATCH 054/355] Add option for no workspace switch on click In sway/workspaces, just like disable-scroll turns on/off the ability to change workspaces by scrolling the mouse add disable-click that turns on/off the ability to change workspaces by clicking. --- man/waybar-sway-workspaces.5.scd | 5 +++++ src/modules/sway/workspaces.cpp | 32 +++++++++++++++++--------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 56b703a..5641182 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -31,6 +31,11 @@ Addressed by *sway/workspaces* default: false ++ If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled. +*disable-click*: ++ + typeof: bool ++ + default: false ++ + If set to false, you can click to change workspace. If set to true this behaviour is disabled. + *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 7774351..6775a21 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -249,22 +249,24 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { auto &&button = pair.first->second; box_.pack_start(button, false, false, 0); button.set_relief(Gtk::RELIEF_NONE); - button.signal_pressed().connect([this, node] { - try { - if (node["target_output"].isString()) { - ipc_.sendCmd( - IPC_COMMAND, - fmt::format(workspace_switch_cmd_ + "; move workspace to output \"{}\"; " + workspace_switch_cmd_, - node["name"].asString(), - node["target_output"].asString(), - node["name"].asString())); - } else { - ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, node["name"].asString())); + if (!config_["disable-click"].asBool()) { + button.signal_pressed().connect([this, node] { + try { + if (node["target_output"].isString()) { + ipc_.sendCmd( + IPC_COMMAND, + fmt::format(workspace_switch_cmd_ + "; move workspace to output \"{}\"; " + workspace_switch_cmd_, + node["name"].asString(), + node["target_output"].asString(), + node["name"].asString())); + } else { + ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, node["name"].asString())); + } + } catch (const std::exception &e) { + spdlog::error("Workspaces: {}", e.what()); } - } catch (const std::exception &e) { - spdlog::error("Workspaces: {}", e.what()); - } - }); + }); + } return button; } From 29fa74f621683720ac8a180c2fdbdd180046477b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Wed, 12 Aug 2020 11:41:00 +0100 Subject: [PATCH 055/355] Add IDs to sway workspace buttons for CSS styling In case you want to style a specific workspace add IDs to the workspace buttons. Styling is done by matching button#sway-workspace-${name}. --- man/waybar-sway-workspaces.5.scd | 1 + src/modules/sway/workspaces.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 5641182..7147d71 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -139,3 +139,4 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge - *#workspaces button.urgent* - *#workspaces button.persistent* - *#workspaces button.current_output* +- *#workspaces button#sway-workspace-${name} diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 6775a21..17a0be2 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -248,6 +248,7 @@ 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_name("sway-workspace-" + node["name"].asString()); button.set_relief(Gtk::RELIEF_NONE); if (!config_["disable-click"].asBool()) { button.signal_pressed().connect([this, node] { From 0aa8c03beaaed42fccb11b55ae09adcf5641229b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Thu, 13 Aug 2020 20:11:55 +0100 Subject: [PATCH 056/355] Add missing * in man page --- man/waybar-sway-workspaces.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 7147d71..5e51689 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -139,4 +139,4 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge - *#workspaces button.urgent* - *#workspaces button.persistent* - *#workspaces button.current_output* -- *#workspaces button#sway-workspace-${name} +- *#workspaces button#sway-workspace-${name}* From 8cd6e1330894c6371e49a61ca0f76f72d016dd88 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Wed, 5 Aug 2020 20:31:36 -0400 Subject: [PATCH 057/355] clock: allow custom formatting for today in calendar --- man/waybar-clock.5.scd | 5 +++++ src/modules/clock.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 3610f19..79555bc 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -31,6 +31,11 @@ The *clock* module displays the current date and time. default: inferred from current locale ++ A locale to be used to display the time. Intended to render times in custom timezones with the proper language and format. +*today-format*: ++ + typeof: string ++ + default: {} ++ + The format of today's date in the calendar. + *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index f41126b..cfdeda2 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -99,7 +99,12 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str os << '\n'; } if (d == curr_day) { - os << "" << date::format("%e", d) << ""; + if (config_["today-format"].isString()) { + auto today_format = config_["today-format"].asString(); + os << fmt::format(today_format, date::format("%e", d)); + } else { + os << "" << date::format("%e", d) << ""; + } } else { os << date::format("%e", d); } From 62082bdb0121ea738f9b8723221572899c19a872 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Wed, 12 Aug 2020 22:46:51 -0400 Subject: [PATCH 058/355] clock: scroll through multiple timezones --- include/ALabel.hpp | 2 +- include/modules/clock.hpp | 3 +++ man/waybar-clock.5.scd | 5 +++++ src/ALabel.cpp | 5 +++-- src/modules/clock.cpp | 36 +++++++++++++++++++++++++++++++++++- 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/include/ALabel.hpp b/include/ALabel.hpp index d4ad94d..6848d67 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -10,7 +10,7 @@ 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); + uint16_t interval = 0, bool ellipsize = false, bool enable_click = false, bool enable_scroll = false); virtual ~ALabel() = default; virtual auto update() -> void; virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0); diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index e3873a6..643b736 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -28,9 +28,12 @@ class Clock : public ALabel { std::locale locale_; const date::time_zone* time_zone_; bool fixed_time_zone_; + int time_zone_idx_; date::year_month_day cached_calendar_ymd_; std::string cached_calendar_text_; + bool handleScroll(GdkEventScroll* e); + auto calendar_text(const waybar_time& wtime) -> std::string; auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void; auto first_day_of_week() -> date::weekday; diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 79555bc..9f36c43 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -26,6 +26,11 @@ The *clock* module displays the current date and time. default: inferred local timezone ++ The timezone to display the time in, e.g. America/New_York. +*timezones*: ++ + typeof: list of strings ++ + A list of timezones to use for time display, changed using the scroll wheel. ++ + Use "" to represent the system's local timezone. Using %Z in the format or tooltip format is useful to track which time zone is currently displayed. + *locale*: ++ typeof: string ++ default: inferred from current locale ++ diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 68313e0..00fa2f7 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -5,8 +5,9 @@ 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()), + const std::string& format, uint16_t interval, bool ellipsize, bool enable_click, + bool enable_scroll) + : AModule(config, name, id, config["format-alt"].isString() || enable_click, enable_scroll), format_(config_["format"].isString() ? config_["format"].asString() : format), interval_(config_["interval"] == "once" ? std::chrono::seconds(100000000) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index cfdeda2..f313606 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -13,7 +13,7 @@ using waybar::modules::waybar_time; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) - : ALabel(config, "clock", id, "{:%H:%M}", 60), fixed_time_zone_(false) { + : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), fixed_time_zone_(false) { if (config_["timezone"].isString()) { spdlog::warn("As using a timezone, some format args may be missing as the date library havn't got a release since 2018."); time_zone_ = date::locate_zone(config_["timezone"].asString()); @@ -71,6 +71,40 @@ auto waybar::modules::Clock::update() -> void { ALabel::update(); } +bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) { + // defer to user commands if set + if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { + return AModule::handleScroll(e); + } + + auto dir = AModule::getScrollDir(e); + if (dir != SCROLL_DIR::UP && dir != SCROLL_DIR::DOWN) { + return true; + } + if (!config_["timezones"].isArray() || config_["timezones"].empty()) { + return true; + } + auto nr_zones = config_["timezones"].size(); + int new_idx = time_zone_idx_ + ((dir == SCROLL_DIR::UP) ? 1 : -1); + if (new_idx < 0) { + time_zone_idx_ = nr_zones - 1; + } else if (new_idx >= nr_zones) { + time_zone_idx_ = 0; + } else { + time_zone_idx_ = new_idx; + } + auto zone_name = config_["timezones"][time_zone_idx_]; + if (!zone_name.isString() || zone_name.empty()) { + fixed_time_zone_ = false; + } else { + time_zone_ = date::locate_zone(zone_name.asString()); + fixed_time_zone_ = true; + } + + update(); + return true; +} + auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string { const auto daypoint = date::floor(wtime.ztime.get_local_time()); const auto ymd = date::year_month_day(daypoint); From fdfb60c633681908e81337ce793350588add2478 Mon Sep 17 00:00:00 2001 From: wjoe Date: Fri, 14 Aug 2020 20:56:45 +0200 Subject: [PATCH 059/355] meson feature: make rfkill optional --- meson.build | 12 ++++++++++-- meson_options.txt | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 56b45be..16da957 100644 --- a/meson.build +++ b/meson.build @@ -137,12 +137,10 @@ if is_linux add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp') src_files += files( 'src/modules/battery.cpp', - 'src/modules/bluetooth.cpp', 'src/modules/cpu/common.cpp', 'src/modules/cpu/linux.cpp', 'src/modules/memory/common.cpp', 'src/modules/memory/linux.cpp', - 'src/util/rfkill.cpp' ) elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp') @@ -207,6 +205,16 @@ if gtk_layer_shell.found() add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp') endif +if get_option('rfkill').enabled() + if is_linux + add_project_arguments('-DWANT_RFKILL', language: 'cpp') + src_files += files( + 'src/modules/bluetooth.cpp', + 'src/util/rfkill.cpp' + ) + endif +endif + subdir('protocol') executable( diff --git a/meson_options.txt b/meson_options.txt index a44ff64..de47da7 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -7,3 +7,4 @@ option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable supp 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('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support') +option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL') From 4565f7f8b9ade0e43057cbd47b3f14b218fd589e Mon Sep 17 00:00:00 2001 From: wjoe Date: Fri, 14 Aug 2020 20:58:48 +0200 Subject: [PATCH 060/355] only compile rfkill into the network module if the feature is enabled. --- include/modules/network.hpp | 4 ++++ src/modules/network.cpp | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index a0156fb..4441dc0 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -11,7 +11,9 @@ #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" +#ifdef WANT_RFKILL #include "util/rfkill.hpp" +#endif namespace waybar::modules { @@ -70,9 +72,11 @@ class Network : public ALabel { util::SleeperThread thread_; util::SleeperThread thread_timer_; +#ifdef WANT_RFKILL util::SleeperThread thread_rfkill_; util::Rfkill rfkill_; +#endif }; } // namespace waybar::modules diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 2c0562f..4e96cef 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -4,7 +4,9 @@ #include #include #include "util/format.hpp" +#ifdef WANT_RFKILL #include "util/rfkill.hpp" +#endif namespace { @@ -84,8 +86,10 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf cidr_(-1), signal_strength_dbm_(0), signal_strength_(0), - frequency_(0), - rfkill_{RFKILL_TYPE_WLAN} { +#ifdef WANT_RFKILL + rfkill_{RFKILL_TYPE_WLAN}, +#endif + 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) { @@ -174,6 +178,7 @@ void waybar::modules::Network::worker() { } thread_timer_.sleep_for(interval_); }; +#ifdef WANT_RFKILL thread_rfkill_ = [this] { rfkill_.waitForEvent(); { @@ -184,14 +189,19 @@ void waybar::modules::Network::worker() { } } }; +#else + spdlog::warn("Waybar has been built without rfkill support."); +#endif } const std::string waybar::modules::Network::getNetworkState() const { +#ifdef WANT_RFKILL if (ifid_ == -1) { if (rfkill_.getState()) return "disabled"; return "disconnected"; } +#endif if (ipaddr_.empty()) return "linked"; if (essid_.empty()) return "ethernet"; return "wifi"; From 4d775008df1cabaa6a9cfd56dfb75fa5d83d54b1 Mon Sep 17 00:00:00 2001 From: wjoe Date: Fri, 14 Aug 2020 20:59:30 +0200 Subject: [PATCH 061/355] only return a bluetooth module from factory if the rfkill feature is enabled. --- include/factory.hpp | 4 +++- src/factory.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/factory.hpp b/include/factory.hpp index fcbf3a2..ebc2359 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -43,7 +43,9 @@ #include "modules/custom.hpp" #include "modules/temperature.hpp" #if defined(__linux__) -#include "modules/bluetooth.hpp" +# ifdef WANT_RFKILL +# include "modules/bluetooth.hpp" +# endif #endif namespace waybar { diff --git a/src/factory.cpp b/src/factory.cpp index 5a01d52..af93b20 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -81,9 +81,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { return new waybar::modules::Temperature(id, config_[name]); } #if defined(__linux__) +# ifdef WANT_RFKILL if (ref == "bluetooth") { return new waybar::modules::Bluetooth(id, config_[name]); } +# endif #endif if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { return new waybar::modules::Custom(ref.substr(7), id, config_[name]); From 3663b9193dc6e3100fa02c472e5b068e988a8f81 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 9 Aug 2020 18:58:01 -0700 Subject: [PATCH 062/355] refactor(bar): separate GTK event handlers for gtk-layer-shell Cleanly separate resizing logic for gtk-layer-shell and manually managed layer surface code. --- include/bar.hpp | 7 ++- src/bar.cpp | 127 +++++++++++++++++++++++++++++++----------------- 2 files changed, 89 insertions(+), 45 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 63f0e22..fdc5a73 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -53,13 +53,18 @@ class Bar { static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *); #ifdef HAVE_GTK_LAYER_SHELL + /* gtk-layer-shell code */ void initGtkLayerShell(); + void onConfigureGLS(GdkEventConfigure *ev); + void onMapGLS(GdkEventAny *ev); #endif + /* fallback layer-surface code */ 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); + /* common code */ + void setExclusiveZone(uint32_t width, uint32_t height); auto setupWidgets() -> void; void getModules(const Factory &, const std::string &); void setupAltFormatKeyForModule(const std::string &module_name); diff --git a/src/bar.cpp b/src/bar.cpp index 3bbc2a3..45e3420 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -101,56 +101,25 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) use_gls_ = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true; if (use_gls_) { initGtkLayerShell(); + window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMapGLS)); + window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigureGLS)); } #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)); + if (!use_gls_) { + 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()) { + if (!use_gls_ && 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(); @@ -181,8 +150,80 @@ void waybar::Bar::initGtkLayerShell() { setExclusiveZone(width_, height_); } } + +void waybar::Bar::onConfigureGLS(GdkEventConfigure* ev) { + /* + * GTK wants new size for the window. + * Actual resizing is done within the gtk-layer-shell code; the only remaining action is to apply + * exclusive zone. + * gtk_layer_auto_exclusive_zone_enable() could handle even that, but at the cost of ignoring + * margins on unanchored edge. + * + * Note: forced resizing to a window smaller than required by GTK would not work with + * gtk-layer-shell. + */ + if (vertical) { + if (width_ > 1 && ev->width > static_cast(width_)) { + spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); + } + } else { + if (!vertical && height_ > 1 && ev->height > static_cast(height_)) { + spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); + } + } + width_ = ev->width; + height_ = ev->height; + spdlog::info(BAR_SIZE_MSG, width_, height_, output->name); + setExclusiveZone(width_, height_); +} + +void waybar::Bar::onMapGLS(GdkEventAny* ev) { + /* + * Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor). + */ + auto gdk_window = window.get_window(); + surface = gdk_wayland_window_get_wl_surface(gdk_window->gobj()); +} + #endif +void waybar::Bar::onConfigure(GdkEventConfigure* ev) { + /* + * GTK wants new size for the window. + * + * Prefer configured size if it's non-default. + * If the size is not set and the window is smaller than requested by GTK, request resize from + * layer surface. + */ + auto tmp_height = height_; + auto tmp_width = width_; + if (ev->height > static_cast(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 (tmp_width != width_ || tmp_height != height_) { + setSurfaceSize(tmp_width, tmp_height); + } +} + void waybar::Bar::onRealize() { auto gdk_window = window.get_window()->gobj(); gdk_wayland_window_set_use_custom_surface(gdk_window); @@ -192,10 +233,6 @@ 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()); @@ -362,7 +399,9 @@ auto waybar::Bar::toggle() -> void { window.set_opacity(1); } setExclusiveZone(width_, height_); - wl_surface_commit(surface); + if (!use_gls_) { + wl_surface_commit(surface); + } } void waybar::Bar::getModules(const Factory& factory, const std::string& pos) { From f4e15dd93d589ed4efb10bad5035476ca623f5cb Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 14 Aug 2020 23:53:44 -0700 Subject: [PATCH 063/355] chore(subprojects): update gtk-layer-shell to 0.3.0 Fixes warning about xdg_wm_base.version mismatch. Fixes potential crash when GTK does not expect wl_surface to be committed. --- subprojects/gtk-layer-shell.wrap | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/subprojects/gtk-layer-shell.wrap b/subprojects/gtk-layer-shell.wrap index 9db9df5..6fe68c3 100644 --- a/subprojects/gtk-layer-shell.wrap +++ b/subprojects/gtk-layer-shell.wrap @@ -1,5 +1,5 @@ [wrap-file] -directory = gtk-layer-shell-0.2.0 -source_filename = gtk-layer-shell-0.2.0.tar.gz -source_hash = 6934376b5296d079fca2c1ba6b222ec91db6bf3667142340ee2bdebfb4b69116 -source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.2.0/gtk-layer-shell-0.2.0.tar.gz +directory = gtk-layer-shell-0.3.0 +source_filename = gtk-layer-shell-0.3.0.tar.gz +source_hash = edd5e31279d494df66da9e9190c219fa295da547f5538207685e98468dbc134d +source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.3.0/gtk-layer-shell-0.3.0.tar.gz From 033f0b01b7d7aefd8867320e569344de5713f57f Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 15 Aug 2020 10:36:15 +0200 Subject: [PATCH 064/355] Fix rfkill condition --- src/modules/network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 4e96cef..ab9b2e5 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -195,13 +195,13 @@ void waybar::modules::Network::worker() { } const std::string waybar::modules::Network::getNetworkState() const { -#ifdef WANT_RFKILL if (ifid_ == -1) { +#ifdef WANT_RFKILL if (rfkill_.getState()) return "disabled"; +#endif return "disconnected"; } -#endif if (ipaddr_.empty()) return "linked"; if (essid_.empty()) return "ethernet"; return "wifi"; From b54fb247456e3e4980891a8c9e6e545d5f808597 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 16 Aug 2020 15:54:21 +0300 Subject: [PATCH 065/355] Remove trim usage in format Some clang-tidy fixes --- include/modules/wlr/taskbar.hpp | 2 +- src/modules/wlr/taskbar.cpp | 20 +++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 53a2f8c..7085d79 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -70,7 +70,7 @@ class Task std::string title_; std::string app_id_; - uint32_t state_; + uint32_t state_ = 0; private: std::string repr() const; diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 23a9166..5c0dcb3 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -49,8 +49,8 @@ static std::vector search_prefix() auto xdg_data_dirs = std::getenv("XDG_DATA_DIRS"); if (!xdg_data_dirs) { - prefixes.push_back("/usr/share/"); - prefixes.push_back("/usr/local/share/"); + prefixes.emplace_back("/usr/share/"); + prefixes.emplace_back("/usr/local/share/"); } else { std::string xdg_data_dirs_str(xdg_data_dirs); size_t start = 0, end = 0; @@ -102,7 +102,7 @@ static std::string get_from_desktop_app_info(const std::string &app_id) } /* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */ -static std::string get_from_icon_theme(Glib::RefPtr icon_theme, +static std::string get_from_icon_theme(const Glib::RefPtr& icon_theme, const std::string &app_id) { if (icon_theme->lookup_icon(app_id, 24)) @@ -111,7 +111,7 @@ static std::string get_from_icon_theme(Glib::RefPtr icon_theme, return ""; } -static bool image_load_icon(Gtk::Image& image, Glib::RefPtr icon_theme, +static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr& icon_theme, const std::string &app_id_list, int size) { std::string app_id; @@ -231,13 +231,13 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, auto icon_pos = format.find("{icon}"); if (icon_pos == 0) { with_icon_ = true; - format_after_ = trim(format.substr(6)); + format_after_ = format.substr(6); } else if (icon_pos == std::string::npos) { format_before_ = format; } else { with_icon_ = true; - format_before_ = trim(format.substr(0, icon_pos)); - format_after_ = trim(format.substr(icon_pos + 6)); + format_before_ = format.substr(0, icon_pos); + format_after_ = format.substr(icon_pos + 6); } } else { /* The default is to only show the icon */ @@ -360,7 +360,7 @@ void Task::handle_output_leave(struct wl_output *output) void Task::handle_state(struct wl_array *state) { state_ = 0; - for (uint32_t* entry = static_cast(state->data); + for (auto* entry = static_cast(state->data); entry < static_cast(state->data) + state->size; entry++) { if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) @@ -709,9 +709,7 @@ bool Taskbar::show_output(struct wl_output *output) const bool Taskbar::all_outputs() const { - static bool result = config_["all_outputs"].isBool() ? config_["all_outputs"].asBool() : false; - - return result; + return config_["all_outputs"].isBool() && config_["all_outputs"].asBool(); } std::vector> Taskbar::icon_themes() const From d263607b27af6b5ca2c0cb59a0de319c7e2009af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C4=8Cernoch?= Date: Tue, 18 Aug 2020 23:09:35 +0200 Subject: [PATCH 066/355] network: fix typo - update tooltip only when it changes --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index ab9b2e5..3914120 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -287,7 +287,7 @@ auto waybar::modules::Network::update() -> void { 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) { + if (label_.get_tooltip_text() != tooltip_text) { label_.set_tooltip_text(tooltip_text); } } else if (label_.get_tooltip_text() != text) { From 8fb54f47ea5052448757abb35e4cf7c001b97343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C4=8Cernoch?= Date: Wed, 19 Aug 2020 23:11:16 +0200 Subject: [PATCH 067/355] battery: allow custom tooltip format --- src/modules/battery.cpp | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index beb0554..999e24e 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -169,22 +169,37 @@ auto waybar::modules::Battery::update() -> void { 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); - } + auto status_pretty = status; // Transform to lowercase and replace space with dash std::transform(status.begin(), status.end(), status.begin(), [](char ch) { return ch == ' ' ? '-' : std::tolower(ch); }); auto format = format_; auto state = getState(capacity, true); + auto time_remaining_formatted = formatTimeRemaining(time_remaining); + if (tooltipEnabled()) { + std::string tooltip_text_default; + std::string tooltip_format = "{autoTooltip}"; + if (time_remaining != 0) { + std::string time_to = std::string("Time to ") + ((time_remaining > 0) ? "empty" : "full"); + tooltip_text_default = time_to + ": " + time_remaining_formatted; + } else { + tooltip_text_default = status_pretty; + } + if (!state.empty() && config_["tooltip-format-" + status + "-" + state].isString()) { + tooltip_format = config_["tooltip-format-" + status + "-" + state].asString(); + } else if (config_["tooltip-format-" + status].isString()) { + tooltip_format = config_["tooltip-format-" + status].asString(); + } else if (!state.empty() && config_["tooltip-format-" + state].isString()) { + tooltip_format = config_["tooltip-format-" + state].asString(); + } else if (config_["tooltip-format"].isString()) { + tooltip_format = config_["tooltip-format"].asString(); + } + label_.set_tooltip_text(fmt::format(tooltip_format, + fmt::arg("autoTooltip", tooltip_text_default), + fmt::arg("capacity", capacity), + fmt::arg("time", time_remaining_formatted))); + } if (!old_status_.empty()) { label_.get_style_context()->remove_class(old_status_); } @@ -205,7 +220,7 @@ auto waybar::modules::Battery::update() -> void { label_.set_markup(fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("icon", getIcon(capacity, icons)), - fmt::arg("time", formatTimeRemaining(time_remaining)))); + fmt::arg("time", time_remaining_formatted))); } // Call parent update ALabel::update(); From 6f7d7e645aeeadb2c081dc00bf9ecaaebdf2991c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Wed, 19 Aug 2020 22:11:11 +0100 Subject: [PATCH 068/355] Prevent line breaks in ellipsized labels If a label is being ellipsized it doesn't make sense to allow it to use line breaks to have multiple lines. Fixes #827 --- src/ALabel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 00fa2f7..3a4063d 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -22,8 +22,10 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st if (config_["max-length"].isUInt()) { label_.set_max_width_chars(config_["max-length"].asUInt()); label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); + label_.set_single_line_mode(true); } else if (ellipsize && label_.get_max_width_chars() == -1) { label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); + label_.set_single_line_mode(true); } if (config_["rotate"].isUInt()) { From ea722615c46d07f5859d2c6a78caebf1f069bc2e Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Mon, 10 Aug 2020 19:23:16 -0400 Subject: [PATCH 069/355] Allow enabing pango markup in the taskbar string The fix for taskbar tooltips in 6a2d214b55 was incomplete: it causes the label to contain escaped titles. Use set_markup so that GTK decodes markup again, but only if requested by the user (disabling markup is needed if using format strings like "{title:.15}" to avoid terminating the string in the middle of an XML entity). --- man/waybar-wlr-taskbar.5.scd | 5 ++++ src/modules/wlr/taskbar.cpp | 51 ++++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index f044412..55d2afd 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -32,6 +32,11 @@ Addressed by *wlr/taskbar* default: 16 ++ The size of the icon. +*markup*: ++ + typeof: bool ++ + default: false ++ + If set to true, pango markup will be accepted in format and tooltip-format. + *tooltip*: ++ typeof: bool ++ default: true ++ diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index f7e11df..57c88fe 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -306,7 +306,7 @@ std::string Task::state_string(bool shortened) const void Task::handle_title(const char *title) { - title_ = Glib::Markup::escape_text(title); + title_ = title; } void Task::handle_app_id(const char *app_id) @@ -460,38 +460,51 @@ bool Task::operator!=(const Task &o) const void Task::update() { + bool markup = config_["markup"].isBool() ? config_["markup"].asBool() : false; + std::string title = title_; + std::string app_id = app_id_; + if (markup) { + title = Glib::Markup::escape_text(title); + app_id = Glib::Markup::escape_text(app_id); + } if (!format_before_.empty()) { - text_before_.set_label( - fmt::format(format_before_, - fmt::arg("title", title_), - fmt::arg("app_id", app_id_), + auto txt = fmt::format(format_before_, + fmt::arg("title", title), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true)) - ) - ); + ); + if (markup) + text_before_.set_markup(txt); + else + text_before_.set_label(txt); text_before_.show(); } if (!format_after_.empty()) { - text_after_.set_label( - fmt::format(format_after_, - fmt::arg("title", title_), - fmt::arg("app_id", app_id_), + auto txt = fmt::format(format_after_, + fmt::arg("title", title), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true)) - ) - ); + ); + if (markup) + text_after_.set_markup(txt); + else + text_after_.set_label(txt); text_after_.show(); } if (!format_tooltip_.empty()) { - button_.set_tooltip_markup( - fmt::format(format_tooltip_, - fmt::arg("title", title_), - fmt::arg("app_id", app_id_), + auto txt = fmt::format(format_tooltip_, + fmt::arg("title", title), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true)) - ) - ); + ); + if (markup) + button_.set_tooltip_markup(txt); + else + button_.set_tooltip_text(txt); } } From ba78199dd1a8bc7e240ea560e70d6626500eadf1 Mon Sep 17 00:00:00 2001 From: Tamir Zahavi-Brunner Date: Fri, 28 Aug 2020 01:16:41 +0300 Subject: [PATCH 070/355] custom: Fix "restart-interval" This commit fixes the issue where the process would restart immediately and the thread would sleep after the process has restarted, and not before. Fixes #621 --- src/modules/custom.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 5643160..5ee9bb5 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -50,7 +50,6 @@ void waybar::modules::Custom::continuousWorker() { thread_ = [this, cmd] { char* buff = nullptr; size_t len = 0; - bool restart = false; if (getline(&buff, &len, fp_) == -1) { int exit_code = 1; if (fp_) { @@ -63,8 +62,8 @@ void waybar::modules::Custom::continuousWorker() { spdlog::error("{} stopped unexpectedly, is it endless?", name_); } if (config_["restart-interval"].isUInt()) { - restart = true; pid_ = -1; + thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt())); fp_ = util::command::open(cmd, pid_); if (!fp_) { throw std::runtime_error("Unable to open " + cmd); @@ -83,9 +82,6 @@ void waybar::modules::Custom::continuousWorker() { output_ = {0, output}; dp.emit(); } - if (restart) { - thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt())); - } }; } From 943b6bc51bbe16c8db72a7b16cabd1aa95c1b853 Mon Sep 17 00:00:00 2001 From: Renee D'Netto Date: Thu, 27 Aug 2020 22:07:19 +1000 Subject: [PATCH 071/355] Implement support for reloading of config files. Fixes #759. --- include/client.hpp | 1 + src/client.cpp | 9 ++++----- src/main.cpp | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index 39b6ae3..37281a2 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -14,6 +14,7 @@ class Client { public: static Client *inst(); int main(int argc, char *argv[]); + void reset(); Glib::RefPtr gtk_app; Glib::RefPtr gdk_display; diff --git a/src/client.cpp b/src/client.cpp index 316e7ec..042f61d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -269,10 +269,9 @@ int waybar::Client::main(int argc, char *argv[]) { 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; } + +void waybar::Client::reset() { + gtk_app->quit(); +} diff --git a/src/main.cpp b/src/main.cpp index 19a8de1..13a2567 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ std::mutex reap_mtx; std::list reap; +volatile bool reload; void* signalThread(void* args) { int err, signum; @@ -70,12 +71,19 @@ void startSignalThread(void) { 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(); } }); + std::signal(SIGUSR2, [](int /*signal*/) { + spdlog::info("Reloading..."); + reload = true; + waybar::Client::inst()->reset(); + }); + for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) { std::signal(sig, [](int sig) { for (auto& bar : waybar::Client::inst()->bars) { @@ -85,7 +93,12 @@ int main(int argc, char* argv[]) { } startSignalThread(); - auto ret = client->main(argc, argv); + auto ret = 0; + do { + reload = false; + ret = client->main(argc, argv); + } while (reload); + delete client; return ret; } catch (const std::exception& e) { From 1b22e2b3200de7fdb94e52e33f3e8846587c98db Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Sat, 29 Aug 2020 22:56:26 -0700 Subject: [PATCH 072/355] style(workspaces): align text with other modules Currently, the bottom border on workspace buttons eats into the box size and causes the text to sit higher than in other modules. This is ugly when there are other modules (like the window title) right next to the workspace module. To fix the issue, create the bottom border using an inset box-shadow, which doesn't affect the box's content sizing. --- resources/style.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/style.css b/resources/style.css index e21ae00..c454bff 100644 --- a/resources/style.css +++ b/resources/style.css @@ -41,19 +41,19 @@ window#waybar.chromium { padding: 0 5px; background-color: transparent; color: #ffffff; - border-bottom: 3px solid transparent; + /* Use box-shadow instead of border so the text isn't offset */ + box-shadow: inset 0 -3px transparent; } /* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ #workspaces button:hover { background: rgba(0, 0, 0, 0.2); - box-shadow: inherit; - border-bottom: 3px solid #ffffff; + box-shadow: inset 0 -3px #ffffff; } #workspaces button.focused { background-color: #64727D; - border-bottom: 3px solid #ffffff; + box-shadow: inset 0 -3px #ffffff; } #workspaces button.urgent { From 225a0eccddd777c474d5efb720611dd5589df914 Mon Sep 17 00:00:00 2001 From: MusiKid Date: Wed, 2 Sep 2020 14:25:57 +0200 Subject: [PATCH 073/355] Add support for memory tooltip --- src/modules/memory/common.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index 4875ec8..a332d58 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -36,7 +36,17 @@ auto waybar::modules::Memory::update() -> void { 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)); + if (config_["tooltip-format"].isString()) { + auto tooltip_format = config_["tooltip-format"].asString(); + label_.set_tooltip_text(fmt::format(tooltip_format, + used_ram_percentage, + fmt::arg("total", total_ram_gigabytes), + fmt::arg("percentage", used_ram_percentage), + fmt::arg("used", used_ram_gigabytes), + fmt::arg("avail", available_ram_gigabytes))); + } else { + label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1)); + } } event_box_.show(); } else { From 98b6d7f283f6cefa2fb8e43f0d175041dcb1c943 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Sun, 6 Sep 2020 21:48:42 +0200 Subject: [PATCH 074/355] Fix non-standard usage of Fixes the following build warning with musl libc: In file included from ../src/util/rfkill.cpp:24: /usr/include/sys/poll.h:1:2: warning: #warning redirecting incorrect #include to [-Wcpp] 1 | #warning redirecting incorrect #include to | ^~~~~~~ --- src/util/rfkill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/rfkill.cpp b/src/util/rfkill.cpp index f987f4c..82d29e9 100644 --- a/src/util/rfkill.cpp +++ b/src/util/rfkill.cpp @@ -20,8 +20,8 @@ #include #include +#include #include -#include #include #include From 9e3e4368c733b9d13d78253aacb37173b7ec83cf Mon Sep 17 00:00:00 2001 From: Tamir Zahavi-Brunner Date: Sun, 6 Sep 2020 22:47:34 +0300 Subject: [PATCH 075/355] custom: Add "exec-on-event" config This config allows disabling the default behavior of re-executing the script whenever an event that has a command set is triggered. Fixes #841 --- include/modules/custom.hpp | 1 + man/waybar-custom.5.scd | 6 ++++++ src/modules/custom.cpp | 10 ++++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/modules/custom.hpp b/include/modules/custom.hpp index b8dad9d..7c77145 100644 --- a/include/modules/custom.hpp +++ b/include/modules/custom.hpp @@ -22,6 +22,7 @@ class Custom : public ALabel { void continuousWorker(); void parseOutputRaw(); void parseOutputJson(); + void handleEvent(); bool handleScroll(GdkEventScroll* e); bool handleToggle(GdkEventButton* const& e); diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 121585a..3e820c6 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -22,6 +22,12 @@ Addressed by *custom/* The path to a script, which determines if the script in *exec* should be executed. *exec* will be executed if the exit code of *exec-if* equals 0. +*exec-on-event*: ++ + typeof: bool ++ + default: true ++ + If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after + executing the event command. + *return-type*: ++ typeof: string ++ See *return-type* diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 5ee9bb5..92eedaa 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -91,15 +91,21 @@ void waybar::modules::Custom::refresh(int sig) { } } +void waybar::modules::Custom::handleEvent() { + if (!config_["exec-on-event"].isBool() || config_["exec-on-event"].asBool()) { + thread_.wake_up(); + } +} + bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) { auto ret = ALabel::handleScroll(e); - thread_.wake_up(); + handleEvent(); return ret; } bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) { auto ret = ALabel::handleToggle(e); - thread_.wake_up(); + handleEvent(); return ret; } From c65167022223abc8e86650924c7da7e1b1a4b19d Mon Sep 17 00:00:00 2001 From: koffeinfriedhof Date: Sun, 13 Sep 2020 17:32:00 +0200 Subject: [PATCH 076/355] Added song position and queue length. --- man/waybar-mpd.5.scd | 4 ++++ resources/config | 2 +- src/modules/mpd.cpp | 7 +++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index 1ee7a98..e8105de 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -148,6 +148,10 @@ Addressed by *mpd* *{totalTime}*: The length of the current song. To format as a date/time (see example configuration) +*{songPosition}*: The position of the current song. + +*{queueLength}*: The length of the current queue. + *{stateIcon}*: The icon corresponding the playing or paused status of the player (see *state-icons* option) *{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option) diff --git a/resources/config b/resources/config index 832f76c..36c4f96 100644 --- a/resources/config +++ b/resources/config @@ -27,7 +27,7 @@ "format": "{}" }, "mpd": { - "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ", + "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ ", "format-disconnected": "Disconnected ", "format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ", "unknown-tag": "N/A", diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index d2877f3..26878b1 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -133,6 +133,7 @@ void waybar::modules::MPD::setLabel() { auto format = format_; std::string artist, album_artist, album, title, date; + int song_pos, queue_length; std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; @@ -161,6 +162,8 @@ void waybar::modules::MPD::setLabel() { album = getTag(MPD_TAG_ALBUM); title = getTag(MPD_TAG_TITLE); date = getTag(MPD_TAG_DATE); + song_pos = mpd_status_get_song_pos(status_.get()); + queue_length = mpd_status_get_queue_length(status_.get()); elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get())); totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get())); } @@ -184,6 +187,8 @@ void waybar::modules::MPD::setLabel() { fmt::arg("date", Glib::Markup::escape_text(date).raw()), fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), + fmt::arg("songPosition", song_pos), + fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon), @@ -200,6 +205,8 @@ void waybar::modules::MPD::setLabel() { fmt::arg("album", album), fmt::arg("title", title), fmt::arg("date", date), + fmt::arg("songPosition", song_pos), + fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon), From 95f505a457def2f148d6aa9795dd3f4e6264c061 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 21 Sep 2020 10:56:40 +0200 Subject: [PATCH 077/355] revert: restore eventfd --- include/modules/network.hpp | 2 ++ src/modules/network.cpp | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 4441dc0..c02d3c5 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -54,6 +54,8 @@ class Network : public ALabel { 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_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 3914120..74ae913 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -83,6 +83,8 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf : 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), @@ -119,6 +121,12 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf } waybar::modules::Network::~Network() { + if (ev_fd_ > -1) { + close(ev_fd_); + } + if (efd_ > -1) { + close(efd_); + } if (ev_sock_ != nullptr) { nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK); if (family_ == AF_INET) { @@ -150,6 +158,30 @@ void waybar::modules::Network::createEventSocket() { } 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() { @@ -192,6 +224,19 @@ void waybar::modules::Network::worker() { #else spdlog::warn("Waybar has been built without rfkill support."); #endif + 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 { From 6db795401a6c060a1e9c6d2a72be3caa69ad8ed0 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 21 Sep 2020 12:18:42 +0200 Subject: [PATCH 078/355] chore: v0.9.4 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 16da957..acc8a47 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.3', + version: '0.9.4', license: 'MIT', default_options : [ 'cpp_std=c++17', From 12016d35bb09b9628a0a37272557fad4e4b4a7c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorben=20G=C3=BCnther?= Date: Mon, 21 Sep 2020 13:17:11 +0200 Subject: [PATCH 079/355] disk module: add state for percentage_used --- man/waybar-disk.5.scd | 4 ++++ src/modules/disk.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index 1a9320c..431d7c8 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -31,6 +31,10 @@ Addressed by *disk* typeof: integer ++ Positive value to rotate the text label. +*states*: ++ + typeof: array ++ + A number of disk utilization states which get activated on certain percentage thresholds (percentage_used). See *waybar-states(5)*. + *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index 59ffea6..83d612b 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -47,13 +47,14 @@ auto waybar::modules::Disk::update() -> void { auto free = pow_format(stats.f_bavail * stats.f_frsize, "B", true); auto used = pow_format((stats.f_blocks - stats.f_bavail) * stats.f_frsize, "B", true); auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true); + auto percentage_used = (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks; 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("percentage_used", percentage_used) , fmt::arg("total", total) , fmt::arg("path", path_) )); @@ -67,12 +68,13 @@ auto waybar::modules::Disk::update() -> void { , 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("percentage_used", percentage_used) , fmt::arg("total", total) , fmt::arg("path", path_) )); } event_box_.show(); + getState(percentage_used); // Call parent update ALabel::update(); } From 7ba14c2097c43866fca5e20ccb736f7ca854fcfd Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Sat, 26 Sep 2020 15:55:06 -0400 Subject: [PATCH 080/355] Fix "on-click-backward" when "on-click-forward" is not present --- src/AModule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 10bd077..7da942e 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -44,9 +44,9 @@ bool AModule::handleToggle(GdkEventButton* const& e) { 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) { + } else if (config_["on-click-backward"].isString() && e->button == 8) { format = config_["on-click-backward"].asString(); - } else if (config_["on-click-backward"].isString() && e->button == 9) { + } else if (config_["on-click-forward"].isString() && e->button == 9) { format = config_["on-click-forward"].asString(); } if (!format.empty()) { From 83d679bf72da86c9121b285e5ee6323fb3cbd1a1 Mon Sep 17 00:00:00 2001 From: lrhel Date: Sat, 26 Sep 2020 23:06:12 +0000 Subject: [PATCH 081/355] Add format-icons for workspace's name entry --- src/modules/sway/workspaces.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 17a0be2..8d78bf5 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -283,6 +283,8 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node return config_["format-icons"]["persistent"].asString(); } else if (config_["format-icons"][key].isString()) { return config_["format-icons"][key].asString(); + } else if (config_["format-icons"][trimWorkspaceName(key)].isString()) { + return config_["format-icons"][trimWorkspaceName(key)].asString(); } } return name; From e9b5be9adbfcc8982c8fc1117d92a250a682c8a1 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 29 Sep 2020 22:28:39 +0200 Subject: [PATCH 082/355] fix: add global /etc/xdg/waybar back. fixes #714 --- src/client.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index 316e7ec..70cc22c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -162,6 +162,7 @@ std::tuple waybar::Client::getConfigs( "$XDG_CONFIG_HOME/waybar/config", "$HOME/.config/waybar/config", "$HOME/waybar/config", + "/etc/xdg/waybar/config", SYSCONFDIR "/xdg/waybar/config", "./resources/config", }) @@ -170,6 +171,7 @@ std::tuple waybar::Client::getConfigs( "$XDG_CONFIG_HOME/waybar/style.css", "$HOME/.config/waybar/style.css", "$HOME/waybar/style.css", + "/etc/xdg/waybar/style.css", SYSCONFDIR "/xdg/waybar/style.css", "./resources/style.css", }) From 73681a30e5ad70a3308b883deedd2b5ad694d8c6 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 29 Sep 2020 22:31:28 +0200 Subject: [PATCH 083/355] man: add the prefixed path were config is loaded --- man/{waybar.5.scd => waybar.5.scd.in} | 1 + meson.build | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) rename man/{waybar.5.scd => waybar.5.scd.in} (99%) diff --git a/man/waybar.5.scd b/man/waybar.5.scd.in similarity index 99% rename from man/waybar.5.scd rename to man/waybar.5.scd.in index cd64f7d..430b9fc 100644 --- a/man/waybar.5.scd +++ b/man/waybar.5.scd.in @@ -14,6 +14,7 @@ Valid locations for this file are: - *~/.config/waybar/config* - *~/waybar/config* - */etc/xdg/waybar/config* +- *@sysconfdir@/xdg/waybar/config* A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config Also a minimal example configuration can be found on the at the bottom of this man page. diff --git a/meson.build b/meson.build index acc8a47..5ac0986 100644 --- a/meson.build +++ b/meson.build @@ -9,6 +9,8 @@ project( ], ) +fs = import('fs') + compiler = meson.get_compiler('cpp') cpp_args = [] @@ -256,9 +258,20 @@ scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_opti if scdoc.found() scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) sh = find_program('sh', native: true) + + main_manpage = configure_file( + input: 'man/waybar.5.scd.in', + output: 'waybar.5.scd', + configuration: { + 'sysconfdir': join_paths(prefix, sysconfdir) + } + ) + + main_manpage_path = join_paths(meson.build_root(), '@0@'.format(main_manpage)) + mandir = get_option('mandir') man_files = [ - 'waybar.5.scd', + main_manpage_path, 'waybar-backlight.5.scd', 'waybar-battery.5.scd', 'waybar-clock.5.scd', @@ -281,14 +294,18 @@ if scdoc.found() 'waybar-bluetooth.5.scd', ] - foreach filename : man_files - topic = filename.split('.')[-3].split('/')[-1] - section = filename.split('.')[-2] + foreach file : man_files + path = '@0@'.format(file) + basename = fs.name(path) + + topic = basename.split('.')[-3].split('/')[-1] + section = basename.split('.')[-2] output = '@0@.@1@'.format(topic, section) custom_target( output, - input: 'man/@0@'.format(filename), + # drops the 'man' if `path` is an absolute path + input: join_paths('man', path), output: output, command: [ sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output) From e4427cb017f235cda521c4a024bc4da55bf7c976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Rolim?= Date: Sun, 6 Sep 2020 14:44:13 -0300 Subject: [PATCH 084/355] sndio: Add module. - can control sndio: change volume, toggle mute - appearance is somewhat dynamic: takes muted status into account - uses polling inside sleeper thread to update values - uses sioctl_* functions, requires sndio>=1.7.0. --- README.md | 1 + include/factory.hpp | 3 + include/modules/sndio.hpp | 29 +++++++ man/waybar-sndio.5.scd | 83 +++++++++++++++++++ meson.build | 20 +++++ meson_options.txt | 1 + src/factory.cpp | 5 ++ src/modules/sndio.cpp | 167 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 309 insertions(+) create mode 100644 include/modules/sndio.hpp create mode 100644 man/waybar-sndio.5.scd create mode 100644 src/modules/sndio.cpp diff --git a/README.md b/README.md index 74f4660..b104ade 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ libnl [Network module] libappindicator-gtk3 [Tray module] libdbusmenu-gtk3 [Tray module] libmpdclient [MPD module] +libsndio [sndio module] ``` **Build dependencies** diff --git a/include/factory.hpp b/include/factory.hpp index ebc2359..3efe6cb 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -39,6 +39,9 @@ #ifdef HAVE_LIBMPDCLIENT #include "modules/mpd.hpp" #endif +#ifdef HAVE_LIBSNDIO +#include "modules/sndio.hpp" +#endif #include "bar.hpp" #include "modules/custom.hpp" #include "modules/temperature.hpp" diff --git a/include/modules/sndio.hpp b/include/modules/sndio.hpp new file mode 100644 index 0000000..f14d062 --- /dev/null +++ b/include/modules/sndio.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class Sndio : public ALabel { + public: + Sndio(const std::string&, const Json::Value&); + ~Sndio(); + auto update() -> void; + auto set_desc(struct sioctl_desc *, unsigned int) -> void; + auto put_val(unsigned int, unsigned int) -> void; + bool handleScroll(GdkEventScroll *); + bool handleToggle(GdkEventButton* const&); + + private: + util::SleeperThread thread_; + struct sioctl_hdl *hdl_; + std::vector pfds_; + unsigned int addr_; + unsigned int volume_, old_volume_, maxval_; + bool muted_; +}; + +} // namespace waybar::modules diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd new file mode 100644 index 0000000..a61c332 --- /dev/null +++ b/man/waybar-sndio.5.scd @@ -0,0 +1,83 @@ +waybar-sndio(5) + +# NAME + +waybar - sndio module + +# DESCRIPTION + +The *sndio* module displays the current volume reported by sndio(7). + +Additionally, you can control the volume by scrolling *up* or *down* while the +cursor is over the module, and clicking on the module toggles mute. + +# CONFIGURATION + +*format*: ++ + typeof: string ++ + default: {volume}% ++ + The format for how information should be displayed. + +*rotate*: ++ + typeof: integer ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*scroll-step*: ++ + typeof: int ++ + default: 5 ++ + The speed in which to change the volume when scrolling. + +*on-click*: ++ + typeof: string ++ + Command to execute when clicked on the module. + This replaces the default behaviour of toggling mute. + +*on-click-middle*: ++ + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. + +*on-click-right*: ++ + typeof: string ++ + Command to execute when you right clicked on the module. + +*on-update*: ++ + typeof: string ++ + Command to execute when the module is updated. + +*on-scroll-up*: ++ + typeof: string ++ + Command to execute when scrolling up on the module. + This replaces the default behaviour of volume control. + +*on-scroll-down*: ++ + typeof: string ++ + Command to execute when scrolling down on the module. + This replaces the default behaviour of volume control. + +*smooth-scrolling-threshold*: ++ + typeof: double ++ + Threshold to be used when scrolling. + +# FORMAT REPLACEMENTS + +*{volume}*: Volume in percentage. + +*{raw_value}*: Volume as value reported by sndio. + +# EXAMPLES + +``` +"sndio": { + "format": "{raw_value} 🎜", + "scroll-step": 3 +} +``` + +# STYLE + +- *#sndio* +- *#sndio.muted* diff --git a/meson.build b/meson.build index 5ac0986..023894c 100644 --- a/meson.build +++ b/meson.build @@ -96,6 +96,19 @@ 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')) + +libsndio = compiler.find_library('sndio', required: get_option('sndio')) +if libsndio.found() + if not compiler.has_function('sioctl_open', prefix: '#include ', dependencies: libsndio) + if get_option('sndio').enabled() + error('libsndio is too old, required >=1.7.0') + else + warning('libsndio is too old, required >=1.7.0') + libsndio = dependency('', required: false) + endif + endif +endif + gtk_layer_shell = dependency('gtk-layer-shell-0', required: get_option('gtk-layer-shell'), fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep']) @@ -207,6 +220,11 @@ if gtk_layer_shell.found() add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp') endif +if libsndio.found() + add_project_arguments('-DHAVE_LIBSNDIO', language: 'cpp') + src_files += 'src/modules/sndio.cpp' +endif + if get_option('rfkill').enabled() if is_linux add_project_arguments('-DWANT_RFKILL', language: 'cpp') @@ -241,6 +259,7 @@ executable( libepoll, libmpdclient, gtk_layer_shell, + libsndio, tz_dep ], include_directories: [include_directories('include')], @@ -292,6 +311,7 @@ if scdoc.found() 'waybar-states.5.scd', 'waybar-wlr-taskbar.5.scd', 'waybar-bluetooth.5.scd', + 'waybar-sndio.5.scd', ] foreach file : man_files diff --git a/meson_options.txt b/meson_options.txt index de47da7..cb5581b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -8,3 +8,4 @@ option('man-pages', type: 'feature', value: 'auto', description: 'Generate and i option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon') option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support') option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL') +option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio') diff --git a/src/factory.cpp b/src/factory.cpp index af93b20..8a5e825 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -76,6 +76,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "mpd") { return new waybar::modules::MPD(id, config_[name]); } +#endif +#ifdef HAVE_LIBSNDIO + if (ref == "sndio") { + return new waybar::modules::Sndio(id, config_[name]); + } #endif if (ref == "temperature") { return new waybar::modules::Temperature(id, config_[name]); diff --git a/src/modules/sndio.cpp b/src/modules/sndio.cpp new file mode 100644 index 0000000..e72c56f --- /dev/null +++ b/src/modules/sndio.cpp @@ -0,0 +1,167 @@ +#include "modules/sndio.hpp" +#include +#include +#include +#include + +namespace waybar::modules { + +void ondesc(void *arg, struct sioctl_desc *d, int curval) { + auto self = static_cast(arg); + if (d == NULL) { + // d is NULL when the list is done + return; + } + self->set_desc(d, curval); +} + +void onval(void *arg, unsigned int addr, unsigned int val) { + auto self = static_cast(arg); + self->put_val(addr, val); +} + +Sndio::Sndio(const std::string &id, const Json::Value &config) + : ALabel(config, "sndio", id, "{volume}%"), + hdl_(nullptr), + pfds_(0), + addr_(0), + volume_(0), + old_volume_(0), + maxval_(0), + muted_(false) { + hdl_ = sioctl_open(SIO_DEVANY, SIOCTL_READ | SIOCTL_WRITE, 0); + if (hdl_ == nullptr) { + throw std::runtime_error("sioctl_open() failed."); + } + + if(sioctl_ondesc(hdl_, ondesc, this) == 0) { + throw std::runtime_error("sioctl_ondesc() failed."); + } + + sioctl_onval(hdl_, onval, this); + + pfds_.reserve(sioctl_nfds(hdl_)); + + event_box_.show(); + + event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK | Gdk::BUTTON_PRESS_MASK); + event_box_.signal_scroll_event().connect( + sigc::mem_fun(*this, &Sndio::handleScroll)); + event_box_.signal_button_press_event().connect( + sigc::mem_fun(*this, &Sndio::handleToggle)); + + thread_ = [this] { + dp.emit(); + + int nfds = sioctl_pollfd(hdl_, pfds_.data(), POLLIN); + if (nfds == 0) { + throw std::runtime_error("sioctl_pollfd() failed."); + } + while (poll(pfds_.data(), nfds, -1) < 0) { + if (errno != EINTR) { + throw std::runtime_error("poll() failed."); + } + } + + int revents = sioctl_revents(hdl_, pfds_.data()); + if (revents & POLLHUP) { + throw std::runtime_error("disconnected!"); + } + }; +} + +Sndio::~Sndio() { + sioctl_close(hdl_); +} + +auto Sndio::update() -> void { + auto format = format_; + unsigned int vol = 100. * static_cast(volume_) / static_cast(maxval_); + + if (volume_ == 0) { + label_.get_style_context()->add_class("muted"); + } else { + label_.get_style_context()->remove_class("muted"); + } + + label_.set_markup(fmt::format(format, + fmt::arg("volume", vol), + fmt::arg("raw_value", volume_))); + + ALabel::update(); +} + +auto Sndio::set_desc(struct sioctl_desc *d, unsigned int val) -> void { + std::string name{d->func}; + std::string node_name{d->node0.name}; + + if (name == "level" && node_name == "output" && d->type == SIOCTL_NUM) { + // store addr for output.level value, used in put_val + addr_ = d->addr; + maxval_ = d->maxval; + volume_ = val; + } +} + +auto Sndio::put_val(unsigned int addr, unsigned int val) -> void { + if (addr == addr_) { + volume_ = val; + } +} + +bool Sndio::handleScroll(GdkEventScroll *e) { + // change the 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; + } + + int step = 5; + if (config_["scroll-step"].isInt()) { + step = config_["scroll-step"].asInt(); + } + + int new_volume = volume_; + if (muted_) { + new_volume = old_volume_; + } + + if (dir == SCROLL_DIR::UP) { + new_volume += step; + } else if (dir == SCROLL_DIR::DOWN) { + new_volume -= step; + } + new_volume = std::clamp(new_volume, 0, static_cast(maxval_)); + + // quits muted mode if volume changes + muted_ = false; + + sioctl_setval(hdl_, addr_, new_volume); + + return true; +} + +bool Sndio::handleToggle(GdkEventButton* const& e) { + // toggle mute only when no user provided events are configured + if (config_["on-click"].isString()) { + return AModule::handleToggle(e); + } + + muted_ = !muted_; + if (muted_) { + // store old volume to be able to restore it later + old_volume_ = volume_; + sioctl_setval(hdl_, addr_, 0); + } else { + sioctl_setval(hdl_, addr_, old_volume_); + } + + return true; +} + +} /* namespace waybar::modules */ From 1f66b06f93ca740767acc6925ae66c50e5e9a453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Rolim?= Date: Wed, 9 Sep 2020 14:48:27 -0300 Subject: [PATCH 085/355] Dockerfiles/alpine: add sndio-dev. --- Dockerfiles/alpine | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/alpine b/Dockerfiles/alpine index 7b71837..21d1cbb 100644 --- a/Dockerfiles/alpine +++ b/Dockerfiles/alpine @@ -2,4 +2,4 @@ FROM alpine:latest -RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev scdoc +RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc From aa625f51967ad7bb6a71ed018e814892332441e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Rolim?= Date: Sun, 6 Sep 2020 15:15:58 -0300 Subject: [PATCH 086/355] .travis.yml: add sndio to FreeBSD run. Also add necessary environment variables and move to /latest, which has sndio-1.7.0. --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 62f7863..abc739c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,9 @@ jobs: compiler: clang env: before_install: - - sudo pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu + - export CPPFLAGS+=-isystem/usr/local/include LDFLAGS+=-L/usr/local/lib # sndio + - sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf + - sudo pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu sndio libfmt libmpdclient libudev-devd meson pulseaudio scdoc spdlog script: - meson build -Dman-pages=enabled From 22e46ea6cc64e35f7ab1536d3b43a6a523658242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Rolim?= Date: Sun, 4 Oct 2020 02:53:21 -0300 Subject: [PATCH 087/355] sndio: Add reconnection support. --- include/modules/sndio.hpp | 1 + src/modules/sndio.cpp | 62 ++++++++++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/include/modules/sndio.hpp b/include/modules/sndio.hpp index f14d062..32ed706 100644 --- a/include/modules/sndio.hpp +++ b/include/modules/sndio.hpp @@ -18,6 +18,7 @@ class Sndio : public ALabel { bool handleToggle(GdkEventButton* const&); private: + auto connect_to_sndio() -> void; util::SleeperThread thread_; struct sioctl_hdl *hdl_; std::vector pfds_; diff --git a/src/modules/sndio.cpp b/src/modules/sndio.cpp index e72c56f..34c46bd 100644 --- a/src/modules/sndio.cpp +++ b/src/modules/sndio.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace waybar::modules { @@ -20,8 +21,25 @@ void onval(void *arg, unsigned int addr, unsigned int val) { self->put_val(addr, val); } +auto Sndio::connect_to_sndio() -> void { + hdl_ = sioctl_open(SIO_DEVANY, SIOCTL_READ | SIOCTL_WRITE, 0); + if (hdl_ == nullptr) { + throw std::runtime_error("sioctl_open() failed."); + } + + if (sioctl_ondesc(hdl_, ondesc, this) == 0) { + throw std::runtime_error("sioctl_ondesc() failed."); + } + + if (sioctl_onval(hdl_, onval, this) == 0) { + throw std::runtime_error("sioctl_onval() failed."); + } + + pfds_.reserve(sioctl_nfds(hdl_)); +} + Sndio::Sndio(const std::string &id, const Json::Value &config) - : ALabel(config, "sndio", id, "{volume}%"), + : ALabel(config, "sndio", id, "{volume}%", 1), hdl_(nullptr), pfds_(0), addr_(0), @@ -29,18 +47,7 @@ Sndio::Sndio(const std::string &id, const Json::Value &config) old_volume_(0), maxval_(0), muted_(false) { - hdl_ = sioctl_open(SIO_DEVANY, SIOCTL_READ | SIOCTL_WRITE, 0); - if (hdl_ == nullptr) { - throw std::runtime_error("sioctl_open() failed."); - } - - if(sioctl_ondesc(hdl_, ondesc, this) == 0) { - throw std::runtime_error("sioctl_ondesc() failed."); - } - - sioctl_onval(hdl_, onval, this); - - pfds_.reserve(sioctl_nfds(hdl_)); + connect_to_sndio(); event_box_.show(); @@ -65,7 +72,28 @@ Sndio::Sndio(const std::string &id, const Json::Value &config) int revents = sioctl_revents(hdl_, pfds_.data()); if (revents & POLLHUP) { - throw std::runtime_error("disconnected!"); + spdlog::warn("sndio disconnected!"); + sioctl_close(hdl_); + hdl_ = nullptr; + + // reconnection loop + while (thread_.isRunning()) { + try { + connect_to_sndio(); + } catch(std::runtime_error const& e) { + // avoid leaking hdl_ + if (hdl_) { + sioctl_close(hdl_); + hdl_ = nullptr; + } + // rate limiting for the retries + thread_.sleep_for(interval_); + continue; + } + + spdlog::warn("sndio reconnected!"); + break; + } } }; } @@ -116,6 +144,9 @@ bool Sndio::handleScroll(GdkEventScroll *e) { return AModule::handleScroll(e); } + // only try to talk to sndio if connected + if (hdl_ == nullptr) return true; + auto dir = AModule::getScrollDir(e); if (dir == SCROLL_DIR::NONE) { return true; @@ -152,6 +183,9 @@ bool Sndio::handleToggle(GdkEventButton* const& e) { return AModule::handleToggle(e); } + // only try to talk to sndio if connected + if (hdl_ == nullptr) return true; + muted_ = !muted_; if (muted_) { // store old volume to be able to restore it later From 21fdcf41c3aacf381bf4756b6a4ec765e5f8c1db Mon Sep 17 00:00:00 2001 From: Joseph Benden Date: Sat, 3 Oct 2020 22:01:51 -0700 Subject: [PATCH 088/355] mpd: revamped to event-driven, single-threaded Fix MPD connection issues by converting/rewriting module into a state-machine driven system. It is fully single-threaded and uses events for transitioning between states. It supports all features and functionality of the previous MPD module. Signed-off-by: Joseph Benden --- include/factory.hpp | 2 +- include/modules/mpd.hpp | 74 ------ include/modules/mpd/mpd.hpp | 66 ++++++ include/modules/mpd/state.hpp | 217 +++++++++++++++++ include/modules/mpd/state.inl.hpp | 24 ++ meson.build | 3 +- src/modules/{ => mpd}/mpd.cpp | 156 ++++-------- src/modules/mpd/state.cpp | 382 ++++++++++++++++++++++++++++++ 8 files changed, 736 insertions(+), 188 deletions(-) delete mode 100644 include/modules/mpd.hpp create mode 100644 include/modules/mpd/mpd.hpp create mode 100644 include/modules/mpd/state.hpp create mode 100644 include/modules/mpd/state.inl.hpp rename src/modules/{ => mpd}/mpd.cpp (72%) create mode 100644 src/modules/mpd/state.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 3efe6cb..f73c6e1 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -37,7 +37,7 @@ #include "modules/pulseaudio.hpp" #endif #ifdef HAVE_LIBMPDCLIENT -#include "modules/mpd.hpp" +#include "modules/mpd/mpd.hpp" #endif #ifdef HAVE_LIBSNDIO #include "modules/sndio.hpp" diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp deleted file mode 100644 index d08b28b..0000000 --- a/include/modules/mpd.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#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(); - bool paused(); - - 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 diff --git a/include/modules/mpd/mpd.hpp b/include/modules/mpd/mpd.hpp new file mode 100644 index 0000000..effe633 --- /dev/null +++ b/include/modules/mpd/mpd.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "ALabel.hpp" +#include "modules/mpd/state.hpp" + +namespace waybar::modules { + +class MPD : public ALabel { + friend class detail::Context; + + // State machine + detail::Context context_{this}; + + const std::string module_name_; + + // Not using unique_ptr since we don't manage the pointer + // (It's either nullptr, or from the config) + const char* server_; + const unsigned port_; + + unsigned timeout_; + + detail::unique_connection connection_; + + detail::unique_status status_; + mpd_state state_; + detail::unique_song song_; + + public: + MPD(const std::string&, const Json::Value&); + virtual ~MPD() noexcept = default; + auto update() -> void; + + private: + std::string getTag(mpd_tag_type type, unsigned idx = 0) const; + void setLabel(); + std::string getStateIcon() const; + std::string getOptionIcon(std::string optionName, bool activated) const; + + // GUI-side methods + bool handlePlayPause(GdkEventButton* const&); + void emit() { dp.emit(); } + + // MPD-side, Non-GUI methods. + void tryConnect(); + void checkErrors(mpd_connection* conn); + void fetchState(); + void queryMPD(); + + inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; } + inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; } + inline bool paused() const { return connection_ && state_ == MPD_STATE_PAUSE; } +}; + +#if !defined(MPD_NOINLINE) +#include "modules/mpd/state.inl.hpp" +#endif + +} // namespace waybar::modules diff --git a/include/modules/mpd/state.hpp b/include/modules/mpd/state.hpp new file mode 100644 index 0000000..79e4f63 --- /dev/null +++ b/include/modules/mpd/state.hpp @@ -0,0 +1,217 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "ALabel.hpp" + +namespace waybar::modules { +class MPD; +} // namespace waybar::modules + +namespace waybar::modules::detail { + +using unique_connection = std::unique_ptr; +using unique_status = std::unique_ptr; +using unique_song = std::unique_ptr; + +class Context; + +/// This state machine loosely follows a non-hierarchical, statechart +/// pattern, and includes ENTRY and EXIT actions. +/// +/// The State class is the base class for all other states. The +/// entry and exit methods are automatically called when entering +/// into a new state and exiting from the current state. This +/// includes initially entering (Disconnected class) and exiting +/// Waybar. +/// +/// The following nested "top-level" states are represented: +/// 1. Idle - await notification of MPD activity. +/// 2. All Non-Idle states: +/// 1. Playing - An active song is producing audio output. +/// 2. Paused - The current song is paused. +/// 3. Stopped - No song is actively playing. +/// 3. Disconnected - periodically attempt MPD (re-)connection. +/// +/// NOTE: Since this statechart is non-hierarchical, the above +/// states are flattened into a set. + +class State { + public: + virtual ~State() noexcept = default; + + virtual void entry() noexcept { spdlog::debug("mpd: ignore entry action"); } + virtual void exit() noexcept { spdlog::debug("mpd: ignore exit action"); } + + virtual void play() { spdlog::debug("mpd: ignore play state transition"); } + virtual void stop() { spdlog::debug("mpd: ignore stop state transition"); } + virtual void pause() { spdlog::debug("mpd: ignore pause state transition"); } + + /// Request state update the GUI. + virtual void update() noexcept { spdlog::debug("mpd: ignoring update method request"); } +}; + +class Idle : public State { + Context* const ctx_; + sigc::connection idle_connection_; + + public: + Idle(Context* const ctx) : ctx_{ctx} {} + virtual ~Idle() noexcept { this->exit(); }; + + void entry() noexcept override; + void exit() noexcept override; + + void play() override; + void stop() override; + void pause() override; + void update() noexcept override; + + private: + Idle(const Idle&) = delete; + Idle& operator=(const Idle&) = delete; + + bool on_io(Glib::IOCondition const&); +}; + +class Playing : public State { + Context* const ctx_; + sigc::connection timer_connection_; + + public: + Playing(Context* const ctx) : ctx_{ctx} {} + virtual ~Playing() noexcept { this->exit(); } + + void entry() noexcept override; + void exit() noexcept override; + + void pause() override; + void stop() override; + void update() noexcept override; + + private: + Playing(Playing const&) = delete; + Playing& operator=(Playing const&) = delete; + + bool on_timer(); +}; + +class Paused : public State { + Context* const ctx_; + sigc::connection timer_connection_; + + public: + Paused(Context* const ctx) : ctx_{ctx} {} + virtual ~Paused() noexcept { this->exit(); } + + void entry() noexcept override; + void exit() noexcept override; + + void play() override; + void stop() override; + void update() noexcept override; + + private: + Paused(Paused const&) = delete; + Paused& operator=(Paused const&) = delete; + + bool on_timer(); +}; + +class Stopped : public State { + Context* const ctx_; + sigc::connection timer_connection_; + + public: + Stopped(Context* const ctx) : ctx_{ctx} {} + virtual ~Stopped() noexcept { this->exit(); } + + void entry() noexcept override; + void exit() noexcept override; + + void play() override; + void pause() override; + void update() noexcept override; + + private: + Stopped(Stopped const&) = delete; + Stopped& operator=(Stopped const&) = delete; + + bool on_timer(); +}; + +class Disconnected : public State { + Context* const ctx_; + sigc::connection timer_connection_; + + public: + Disconnected(Context* const ctx) : ctx_{ctx} {} + virtual ~Disconnected() noexcept { this->exit(); } + + void entry() noexcept override; + void exit() noexcept override; + + void update() noexcept override; + + private: + Disconnected(Disconnected const&) = delete; + Disconnected& operator=(Disconnected const&) = delete; + + void arm_timer(int interval) noexcept; + void disarm_timer() noexcept; + + bool on_timer(); +}; + +class Context { + std::unique_ptr state_; + waybar::modules::MPD* mpd_module_; + + friend class State; + friend class Playing; + friend class Paused; + friend class Stopped; + friend class Disconnected; + friend class Idle; + + protected: + void setState(std::unique_ptr&& new_state) noexcept { + if (state_.get() != nullptr) { + state_->exit(); + } + state_ = std::move(new_state); + state_->entry(); + } + + bool is_connected() const; + bool is_playing() const; + bool is_paused() const; + bool is_stopped() const; + constexpr std::size_t interval() const; + void tryConnect() const; + void checkErrors(mpd_connection*) const; + void do_update(); + void queryMPD() const; + void fetchState() const; + constexpr mpd_state state() const; + void emit() const; + [[nodiscard]] unique_connection& connection(); + + public: + explicit Context(waybar::modules::MPD* const mpd_module) + : state_{std::make_unique(this)}, mpd_module_{mpd_module} { + state_->entry(); + } + + void play() { state_->play(); } + void stop() { state_->stop(); } + void pause() { state_->pause(); } + void update() noexcept { state_->update(); } +}; + +} // namespace waybar::modules::detail diff --git a/include/modules/mpd/state.inl.hpp b/include/modules/mpd/state.inl.hpp new file mode 100644 index 0000000..0d83b0b --- /dev/null +++ b/include/modules/mpd/state.inl.hpp @@ -0,0 +1,24 @@ +#pragma once + +namespace detail { + +inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; } +inline bool Context::is_playing() const { return mpd_module_->playing(); } +inline bool Context::is_paused() const { return mpd_module_->paused(); } +inline bool Context::is_stopped() const { return mpd_module_->stopped(); } + +constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_.count(); } +inline void Context::tryConnect() const { mpd_module_->tryConnect(); } +inline unique_connection& Context::connection() { return mpd_module_->connection_; } +constexpr inline mpd_state Context::state() const { return mpd_module_->state_; } + +inline void Context::do_update() { + mpd_module_->setLabel(); +} + +inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); } +inline void Context::queryMPD() const { mpd_module_->queryMPD(); } +inline void Context::fetchState() const { mpd_module_->fetchState(); } +inline void Context::emit() const { mpd_module_->emit(); } + +} // namespace detail diff --git a/meson.build b/meson.build index 023894c..de20382 100644 --- a/meson.build +++ b/meson.build @@ -213,7 +213,8 @@ endif if libmpdclient.found() add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') - src_files += 'src/modules/mpd.cpp' + src_files += 'src/modules/mpd/mpd.cpp' + src_files += 'src/modules/mpd/state.cpp' endif if gtk_layer_shell.found() diff --git a/src/modules/mpd.cpp b/src/modules/mpd/mpd.cpp similarity index 72% rename from src/modules/mpd.cpp rename to src/modules/mpd/mpd.cpp index 26878b1..50f2817 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -1,8 +1,15 @@ -#include "modules/mpd.hpp" +#include "modules/mpd/mpd.hpp" -#include +#include #include +#include "modules/mpd/state.hpp" +#if defined(MPD_NOINLINE) +namespace waybar::modules { +#include "modules/mpd/state.inl.hpp" +} // namespace waybar::modules +#endif + waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) : ALabel(config, "mpd", id, "{album} - {artist} - {title}", 5), module_name_(id.empty() ? "mpd" : "mpd#" + id), @@ -10,7 +17,6 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) 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()) { @@ -28,73 +34,33 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) 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(); + context_.update(); // Call parent update ALabel::update(); } -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) { - if (strcmp(e.what(), "Connection to MPD closed") == 0) { - spdlog::debug("{}: {}", module_name_, e.what()); - } else { - spdlog::warn("{}: {}", module_name_, e.what()); - } - } +void waybar::modules::MPD::queryMPD() { + if (connection_ != nullptr) { + spdlog::debug("{}: fetching state information", module_name_); + try { + fetchState(); + spdlog::debug("{}: fetch complete", module_name_); + } catch (std::exception const& e) { + spdlog::error("{}: {}", module_name_, e.what()); + state_ = MPD_STATE_UNKNOWN; } - }); + + dp.emit(); + } } -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 waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) const { std::string result = config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A"; const char* tag = mpd_song_get_tag(song_.get(), type, idx); @@ -133,7 +99,7 @@ void waybar::modules::MPD::setLabel() { auto format = format_; std::string artist, album_artist, album, title, date; - int song_pos, queue_length; + int song_pos, queue_length; std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; @@ -149,8 +115,8 @@ void waybar::modules::MPD::setLabel() { label_.get_style_context()->add_class("playing"); label_.get_style_context()->remove_class("paused"); } else if (paused()) { - format = - config_["format-paused"].isString() ? config_["format-paused"].asString() : config_["format"].asString(); + format = config_["format-paused"].isString() ? config_["format-paused"].asString() + : config_["format"].asString(); label_.get_style_context()->add_class("paused"); label_.get_style_context()->remove_class("playing"); } @@ -216,7 +182,7 @@ void waybar::modules::MPD::setLabel() { } } -std::string waybar::modules::MPD::getStateIcon() { +std::string waybar::modules::MPD::getStateIcon() const { if (!config_["state-icons"].isObject()) { return ""; } @@ -238,7 +204,7 @@ std::string waybar::modules::MPD::getStateIcon() { } } -std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) { +std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) const { if (!config_[optionName + "-icons"].isObject()) { return ""; } @@ -261,15 +227,11 @@ void waybar::modules::MPD::tryConnect() { } connection_ = - unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); + detail::unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); - alternate_connection_ = - unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); - - if (connection_ == nullptr || alternate_connection_ == nullptr) { + if (connection_ == nullptr) { spdlog::error("{}: Failed to connect to MPD", module_name_); connection_.reset(); - alternate_connection_.reset(); return; } @@ -279,7 +241,6 @@ void waybar::modules::MPD::tryConnect() { } catch (std::runtime_error& e) { spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what()); connection_.reset(); - alternate_connection_.reset(); } } @@ -292,7 +253,6 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) { 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: @@ -306,37 +266,20 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) { } void waybar::modules::MPD::fetchState() { + if (connection_ == nullptr) { + spdlog::error("{}: Not connected to MPD", module_name_); + return; + } + auto conn = connection_.get(); - status_ = unique_status(mpd_run_status(conn), &mpd_status_free); + + status_ = detail::unique_status(mpd_run_status(conn), &mpd_status_free); checkErrors(conn); + 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); - + song_ = detail::unique_song(mpd_run_current_song(conn), &mpd_song_free); checkErrors(conn); } @@ -346,24 +289,13 @@ bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) { } if (e->button == 1) { - std::lock_guard guard(connection_lock_); - if (stopped()) { - mpd_run_play(connection_.get()); - } else { - mpd_run_toggle_pause(connection_.get()); - } + if (state_ == MPD_STATE_PLAY) + context_.pause(); + else + context_.play(); } else if (e->button == 3) { - std::lock_guard guard(connection_lock_); - mpd_run_stop(connection_.get()); + context_.stop(); } return true; } - -bool waybar::modules::MPD::stopped() { - return connection_ == nullptr || state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP || status_ == nullptr; -} - -bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; } - -bool waybar::modules::MPD::paused() { return connection_ != nullptr && state_ == MPD_STATE_PAUSE; } diff --git a/src/modules/mpd/state.cpp b/src/modules/mpd/state.cpp new file mode 100644 index 0000000..21b67ae --- /dev/null +++ b/src/modules/mpd/state.cpp @@ -0,0 +1,382 @@ +#include "modules/mpd/state.hpp" + +#include +#include + +#include "modules/mpd/mpd.hpp" +#if defined(MPD_NOINLINE) +namespace waybar::modules { +#include "modules/mpd/state.inl.hpp" +} // namespace waybar::modules +#endif + +namespace waybar::modules::detail { + +#define IDLE_RUN_NOIDLE_AND_CMD(...) \ + if (idle_connection_.connected()) { \ + idle_connection_.disconnect(); \ + auto conn = ctx_->connection().get(); \ + if (!mpd_run_noidle(conn)) { \ + if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { \ + spdlog::error("mpd: Idle: failed to unregister for IDLE events"); \ + ctx_->checkErrors(conn); \ + } \ + } \ + __VA_ARGS__; \ + } + +void Idle::play() { + IDLE_RUN_NOIDLE_AND_CMD(mpd_run_play(conn)); + + ctx_->setState(std::make_unique(ctx_)); +} + +void Idle::pause() { + IDLE_RUN_NOIDLE_AND_CMD(mpd_run_pause(conn, true)); + + ctx_->setState(std::make_unique(ctx_)); +} + +void Idle::stop() { + IDLE_RUN_NOIDLE_AND_CMD(mpd_run_stop(conn)); + + ctx_->setState(std::make_unique(ctx_)); +} + +#undef IDLE_RUN_NOIDLE_AND_CMD + +void Idle::update() noexcept { + // This is intentionally blank. +} + +void Idle::entry() noexcept { + auto conn = ctx_->connection().get(); + assert(conn != nullptr); + + if (!mpd_send_idle_mask( + conn, static_cast(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) { + ctx_->checkErrors(conn); + spdlog::error("mpd: Idle: failed to register for IDLE events"); + } else { + spdlog::debug("mpd: Idle: watching FD"); + sigc::slot idle_slot = sigc::mem_fun(*this, &Idle::on_io); + idle_connection_ = + Glib::signal_io().connect(idle_slot, + mpd_connection_get_fd(conn), + Glib::IO_IN | Glib::IO_PRI | Glib::IO_ERR | Glib::IO_HUP); + } +} + +void Idle::exit() noexcept { + if (idle_connection_.connected()) { + idle_connection_.disconnect(); + spdlog::debug("mpd: Idle: unwatching FD"); + } +} + +bool Idle::on_io(Glib::IOCondition const&) { + auto conn = ctx_->connection().get(); + + // callback should do this: + enum mpd_idle events = mpd_recv_idle(conn, /* ignore_timeout?= */ false); + spdlog::debug("mpd: Idle: recv_idle events -> {}", events); + + mpd_response_finish(conn); + try { + ctx_->checkErrors(conn); + } catch (std::exception const& e) { + spdlog::warn("mpd: Idle: error: {}", e.what()); + ctx_->setState(std::make_unique(ctx_)); + return false; + } + + ctx_->fetchState(); + mpd_state state = ctx_->state(); + + if (state == MPD_STATE_STOP) { + ctx_->emit(); + ctx_->setState(std::make_unique(ctx_)); + } else if (state == MPD_STATE_PLAY) { + ctx_->emit(); + ctx_->setState(std::make_unique(ctx_)); + } else if (state == MPD_STATE_PAUSE) { + ctx_->emit(); + ctx_->setState(std::make_unique(ctx_)); + } else { + ctx_->emit(); + // self transition + ctx_->setState(std::make_unique(ctx_)); + } + + return false; +} + +void Playing::entry() noexcept { + sigc::slot timer_slot = sigc::mem_fun(*this, &Playing::on_timer); + timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 1'000); + spdlog::debug("mpd: Playing: enabled 1 second periodic timer."); +} + +void Playing::exit() noexcept { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + spdlog::debug("mpd: Playing: disabled 1 second periodic timer."); + } +} + +bool Playing::on_timer() { + // Attempt to connect with MPD. + try { + ctx_->tryConnect(); + + // Success? + if (!ctx_->is_connected()) { + ctx_->setState(std::make_unique(ctx_)); + return false; + } + + ctx_->fetchState(); + + if (!ctx_->is_playing()) { + if (ctx_->is_paused()) { + ctx_->setState(std::make_unique(ctx_)); + } else { + ctx_->setState(std::make_unique(ctx_)); + } + return false; + } + + ctx_->queryMPD(); + ctx_->emit(); + } catch (std::exception const& e) { + spdlog::warn("mpd: Playing: error: {}", e.what()); + ctx_->setState(std::make_unique(ctx_)); + return false; + } + + return true; +} + +void Playing::stop() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_stop(ctx_->connection().get()); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Playing::pause() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_pause(ctx_->connection().get(), true); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Playing::update() noexcept { ctx_->do_update(); } + +void Paused::entry() noexcept { + sigc::slot timer_slot = sigc::mem_fun(*this, &Paused::on_timer); + timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200); + spdlog::debug("mpd: Paused: enabled 200 ms periodic timer."); +} + +void Paused::exit() noexcept { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + spdlog::debug("mpd: Paused: disabled 200 ms periodic timer."); + } +} + +bool Paused::on_timer() { + bool rc = true; + + // Attempt to connect with MPD. + try { + ctx_->tryConnect(); + + // Success? + if (!ctx_->is_connected()) { + ctx_->setState(std::make_unique(ctx_)); + return false; + } + + ctx_->fetchState(); + + ctx_->emit(); + + if (ctx_->is_paused()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } else if (ctx_->is_playing()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } else if (ctx_->is_stopped()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } + } catch (std::exception const& e) { + spdlog::warn("mpd: Paused: error: {}", e.what()); + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } + + return rc; +} + +void Paused::play() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_play(ctx_->connection().get()); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Paused::stop() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_stop(ctx_->connection().get()); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Paused::update() noexcept { ctx_->do_update(); } + +void Stopped::entry() noexcept { + sigc::slot timer_slot = sigc::mem_fun(*this, &Stopped::on_timer); + timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200); + spdlog::debug("mpd: Stopped: enabled 200 ms periodic timer."); +} + +void Stopped::exit() noexcept { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + spdlog::debug("mpd: Stopped: disabled 200 ms periodic timer."); + } +} + +bool Stopped::on_timer() { + bool rc = true; + + // Attempt to connect with MPD. + try { + ctx_->tryConnect(); + + // Success? + if (!ctx_->is_connected()) { + ctx_->setState(std::make_unique(ctx_)); + return false; + } + + ctx_->fetchState(); + + ctx_->emit(); + + if (ctx_->is_stopped()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } else if (ctx_->is_playing()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } else if (ctx_->is_paused()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } + } catch (std::exception const& e) { + spdlog::warn("mpd: Stopped: error: {}", e.what()); + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } + + return rc; +} + +void Stopped::play() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_play(ctx_->connection().get()); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Stopped::pause() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_pause(ctx_->connection().get(), true); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Stopped::update() noexcept { ctx_->do_update(); } + +void Disconnected::arm_timer(int interval) noexcept { + // unregister timer, if present + disarm_timer(); + + // register timer + sigc::slot timer_slot = sigc::mem_fun(*this, &Disconnected::on_timer); + timer_connection_ = + Glib::signal_timeout().connect(timer_slot, interval); + spdlog::debug("mpd: Disconnected: enabled interval timer."); +} + +void Disconnected::disarm_timer() noexcept { + // unregister timer, if present + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + spdlog::debug("mpd: Disconnected: disabled interval timer."); + } +} + +void Disconnected::entry() noexcept { + arm_timer(1'000); +} + +void Disconnected::exit() noexcept { + disarm_timer(); +} + +bool Disconnected::on_timer() { + // Attempt to connect with MPD. + try { + ctx_->tryConnect(); + + // Success? + if (ctx_->is_connected()) { + ctx_->fetchState(); + ctx_->emit(); + + if (ctx_->is_playing()) { + ctx_->setState(std::make_unique(ctx_)); + } else if (ctx_->is_paused()) { + ctx_->setState(std::make_unique(ctx_)); + } else { + ctx_->setState(std::make_unique(ctx_)); + } + + return false; // do not rearm timer + } + } catch (std::exception const& e) { + spdlog::warn("mpd: Disconnected: error: {}", e.what()); + } + + arm_timer(ctx_->interval() * 1'000); + + return false; +} + +void Disconnected::update() noexcept { ctx_->do_update(); } + +} // namespace waybar::modules::detail From cc3acf8102c71d470b00fd55126aef4fb335f728 Mon Sep 17 00:00:00 2001 From: nikto_b Date: Sat, 10 Oct 2020 19:09:18 +0300 Subject: [PATCH 089/355] feature: created sway language submodule; added styles & config part for a sway language submodule --- include/factory.hpp | 1 + include/modules/sway/language.hpp | 28 +++++++++++++ meson.build | 1 + resources/config | 2 +- resources/style.css | 8 ++++ src/factory.cpp | 3 ++ src/modules/sway/language.cpp | 70 +++++++++++++++++++++++++++++++ 7 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 include/modules/sway/language.hpp create mode 100644 src/modules/sway/language.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 3efe6cb..7eed15a 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -6,6 +6,7 @@ #include "modules/sway/mode.hpp" #include "modules/sway/window.hpp" #include "modules/sway/workspaces.hpp" +#include "modules/sway/language.hpp" #endif #ifdef HAVE_WLR #include "modules/wlr/taskbar.hpp" diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp new file mode 100644 index 0000000..7cd6bf6 --- /dev/null +++ b/include/modules/sway/language.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include "ALabel.hpp" +#include "bar.hpp" +#include "client.hpp" +#include "modules/sway/ipc/client.hpp" +#include "util/json.hpp" + +namespace waybar::modules::sway { + +class Language : public ALabel, public sigc::trackable { + public: + Language(const std::string& id, const Json::Value& config); + ~Language() = default; + auto update() -> void; + + private: + void onEvent(const struct Ipc::ipc_response&); + void onCmd(const struct Ipc::ipc_response&); + + std::string lang_; + util::JsonParser parser_; + std::mutex mutex_; + Ipc ipc_; +}; + +} // namespace waybar::modules::sway diff --git a/meson.build b/meson.build index 023894c..e563de0 100644 --- a/meson.build +++ b/meson.build @@ -172,6 +172,7 @@ add_project_arguments('-DHAVE_SWAY', language: 'cpp') src_files += [ 'src/modules/sway/ipc/client.cpp', 'src/modules/sway/mode.cpp', + 'src/modules/sway/language.cpp', 'src/modules/sway/window.cpp', 'src/modules/sway/workspaces.cpp' ] diff --git a/resources/config b/resources/config index 36c4f96..f3c0a77 100644 --- a/resources/config +++ b/resources/config @@ -6,7 +6,7 @@ // 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-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "sway/language", "battery", "battery#bat2", "clock", "tray"], // Modules configuration // "sway/workspaces": { // "disable-scroll": true, diff --git a/resources/style.css b/resources/style.css index e21ae00..a16afd0 100644 --- a/resources/style.css +++ b/resources/style.css @@ -200,3 +200,11 @@ label:focus { #mpd.paused { background-color: #51a37a; } + +#language { + background: #00b093; + color: #740864; + padding: 0 5px; + margin: 0 5px; + min-width: 16px; +} diff --git a/src/factory.cpp b/src/factory.cpp index 8a5e825..1f90789 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -22,6 +22,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "sway/window") { return new waybar::modules::sway::Window(id, bar_, config_[name]); } + if (ref == "sway/language") { + return new waybar::modules::sway::Language(id, config_[name]); + } #endif #ifdef HAVE_WLR if (ref == "wlr/taskbar") { diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp new file mode 100644 index 0000000..a318647 --- /dev/null +++ b/src/modules/sway/language.cpp @@ -0,0 +1,70 @@ +#include "modules/sway/language.hpp" +#include + +namespace waybar::modules::sway { + +Language::Language(const std::string& id, const Json::Value& config) + : ALabel(config, "language", id, "{}", 0, true) { + ipc_.subscribe(R"(["input"])"); + ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent)); + ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd)); + ipc_.sendCmd(IPC_GET_INPUTS); + // Launch worker + ipc_.setWorker([this] { + try { + ipc_.handleEvent(); + } catch (const std::exception& e) { + spdlog::error("Language: {}", e.what()); + } + }); + dp.emit(); +} + +void Language::onCmd(const struct Ipc::ipc_response& res) { + try { + auto payload = parser_.parse(res.payload); + //Display current layout of a device with a maximum count of layouts, expecting that all will be OK + Json::Value::ArrayIndex maxId = 0, max = 0; + for(Json::Value::ArrayIndex i = 0; i < payload.size(); i++) { + if(payload[i]["xkb_layout_names"].size() > max) { + max = payload[i]["xkb_layout_names"].size(); + maxId = i; + } + } + auto layout_name = payload[maxId]["xkb_active_layout_name"].asString().substr(0,2); + lang_ = Glib::Markup::escape_text(layout_name); + dp.emit(); + } catch (const std::exception& e) { + spdlog::error("Language: {}", e.what()); + } +} + +void Language::onEvent(const struct Ipc::ipc_response& res) { + try { + std::lock_guard lock(mutex_); + auto payload = parser_.parse(res.payload)["input"]; + if (payload["type"].asString() == "keyboard") { + auto layout_name = payload["xkb_active_layout_name"].asString().substr(0,2); + lang_ = Glib::Markup::escape_text(layout_name); + } + dp.emit(); + } catch (const std::exception& e) { + spdlog::error("Language: {}", e.what()); + } +} + +auto Language::update() -> void { + if (lang_.empty()) { + event_box_.hide(); + } else { + label_.set_markup(fmt::format(format_, lang_)); + if (tooltipEnabled()) { + label_.set_tooltip_text(lang_); + } + event_box_.show(); + } + // Call parent update + ALabel::update(); +} + +} // namespace waybar::modules::sway From e9b2d275c836a08d8d5eae4cf2766f362c3cb572 Mon Sep 17 00:00:00 2001 From: Christoffer Noerbjerg Date: Sun, 11 Oct 2020 22:36:30 +0200 Subject: [PATCH 090/355] added module group selectors for styling --- src/bar.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bar.cpp b/src/bar.cpp index 45e3420..8af6b97 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -24,6 +24,9 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) window.get_style_context()->add_class(output->name); window.get_style_context()->add_class(config["name"].asString()); window.get_style_context()->add_class(config["position"].asString()); + left_.get_style_context()->add_class("modules-left"); + center_.get_style_context()->add_class("modules-center"); + right_.get_style_context()->add_class("modules-right"); if (config["position"] == "right" || config["position"] == "left") { height_ = 0; From 4229e9b2ca8aaa31bbf624e019b4cf82aed06e68 Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Mon, 12 Oct 2020 02:05:26 +0200 Subject: [PATCH 091/355] Implemented format-{state} for cpu/disk/memory --- src/modules/cpu/common.cpp | 15 +++++++++++++-- src/modules/disk.cpp | 32 +++++++++++++++++++++----------- src/modules/memory/common.cpp | 26 ++++++++++++++++++-------- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index f2204cd..e86d10a 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -15,8 +15,19 @@ auto waybar::modules::Cpu::update() -> void { 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); + auto format = format_; + auto state = getState(cpu_usage); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + label_.set_markup(fmt::format(format, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage))); + } + // Call parent update ALabel::update(); } diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index 83d612b..e63db47 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -49,15 +49,27 @@ auto waybar::modules::Disk::update() -> void { auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true); auto percentage_used = (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks; - label_.set_markup(fmt::format(format_ - , stats.f_bavail * 100 / stats.f_blocks - , fmt::arg("free", free) - , fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks) - , fmt::arg("used", used) - , fmt::arg("percentage_used", percentage_used) - , fmt::arg("total", total) - , fmt::arg("path", path_) - )); + auto format = format_; + auto state = getState(percentage_used); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + label_.set_markup(fmt::format(format + , stats.f_bavail * 100 / stats.f_blocks + , fmt::arg("free", free) + , fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks) + , fmt::arg("used", used) + , fmt::arg("percentage_used", percentage_used) + , fmt::arg("total", total) + , fmt::arg("path", path_) + )); + } + if (tooltipEnabled()) { std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)"; if (config_["tooltip-format"].isString()) { @@ -73,8 +85,6 @@ auto waybar::modules::Disk::update() -> void { , fmt::arg("path", path_) )); } - event_box_.show(); - getState(percentage_used); // Call parent update ALabel::update(); } diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index a332d58..09ce8e8 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -28,13 +28,24 @@ auto waybar::modules::Memory::update() -> void { 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))); + auto format = format_; + auto state = getState(used_ram_percentage); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + label_.set_markup(fmt::format(format, + used_ram_percentage, + fmt::arg("total", total_ram_gigabytes), + fmt::arg("percentage", used_ram_percentage), + fmt::arg("used", used_ram_gigabytes), + fmt::arg("avail", available_ram_gigabytes))); + } + if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { auto tooltip_format = config_["tooltip-format"].asString(); @@ -48,7 +59,6 @@ auto waybar::modules::Memory::update() -> void { label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1)); } } - event_box_.show(); } else { event_box_.hide(); } From 54beabb9dc7ce3bc3a80488c948ca6bfd437336d Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 18 Oct 2020 10:45:31 +0200 Subject: [PATCH 092/355] Revert "mpd: revamped to event-driven, single-threaded" --- include/factory.hpp | 2 +- include/modules/mpd.hpp | 74 ++++++ include/modules/mpd/mpd.hpp | 66 ------ include/modules/mpd/state.hpp | 217 ----------------- include/modules/mpd/state.inl.hpp | 24 -- meson.build | 3 +- src/modules/{mpd => }/mpd.cpp | 156 ++++++++---- src/modules/mpd/state.cpp | 382 ------------------------------ 8 files changed, 188 insertions(+), 736 deletions(-) create mode 100644 include/modules/mpd.hpp delete mode 100644 include/modules/mpd/mpd.hpp delete mode 100644 include/modules/mpd/state.hpp delete mode 100644 include/modules/mpd/state.inl.hpp rename src/modules/{mpd => }/mpd.cpp (72%) delete mode 100644 src/modules/mpd/state.cpp diff --git a/include/factory.hpp b/include/factory.hpp index f73c6e1..3efe6cb 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -37,7 +37,7 @@ #include "modules/pulseaudio.hpp" #endif #ifdef HAVE_LIBMPDCLIENT -#include "modules/mpd/mpd.hpp" +#include "modules/mpd.hpp" #endif #ifdef HAVE_LIBSNDIO #include "modules/sndio.hpp" diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp new file mode 100644 index 0000000..d08b28b --- /dev/null +++ b/include/modules/mpd.hpp @@ -0,0 +1,74 @@ +#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(); + bool paused(); + + 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 diff --git a/include/modules/mpd/mpd.hpp b/include/modules/mpd/mpd.hpp deleted file mode 100644 index effe633..0000000 --- a/include/modules/mpd/mpd.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -#include "ALabel.hpp" -#include "modules/mpd/state.hpp" - -namespace waybar::modules { - -class MPD : public ALabel { - friend class detail::Context; - - // State machine - detail::Context context_{this}; - - const std::string module_name_; - - // Not using unique_ptr since we don't manage the pointer - // (It's either nullptr, or from the config) - const char* server_; - const unsigned port_; - - unsigned timeout_; - - detail::unique_connection connection_; - - detail::unique_status status_; - mpd_state state_; - detail::unique_song song_; - - public: - MPD(const std::string&, const Json::Value&); - virtual ~MPD() noexcept = default; - auto update() -> void; - - private: - std::string getTag(mpd_tag_type type, unsigned idx = 0) const; - void setLabel(); - std::string getStateIcon() const; - std::string getOptionIcon(std::string optionName, bool activated) const; - - // GUI-side methods - bool handlePlayPause(GdkEventButton* const&); - void emit() { dp.emit(); } - - // MPD-side, Non-GUI methods. - void tryConnect(); - void checkErrors(mpd_connection* conn); - void fetchState(); - void queryMPD(); - - inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; } - inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; } - inline bool paused() const { return connection_ && state_ == MPD_STATE_PAUSE; } -}; - -#if !defined(MPD_NOINLINE) -#include "modules/mpd/state.inl.hpp" -#endif - -} // namespace waybar::modules diff --git a/include/modules/mpd/state.hpp b/include/modules/mpd/state.hpp deleted file mode 100644 index 79e4f63..0000000 --- a/include/modules/mpd/state.hpp +++ /dev/null @@ -1,217 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -#include "ALabel.hpp" - -namespace waybar::modules { -class MPD; -} // namespace waybar::modules - -namespace waybar::modules::detail { - -using unique_connection = std::unique_ptr; -using unique_status = std::unique_ptr; -using unique_song = std::unique_ptr; - -class Context; - -/// This state machine loosely follows a non-hierarchical, statechart -/// pattern, and includes ENTRY and EXIT actions. -/// -/// The State class is the base class for all other states. The -/// entry and exit methods are automatically called when entering -/// into a new state and exiting from the current state. This -/// includes initially entering (Disconnected class) and exiting -/// Waybar. -/// -/// The following nested "top-level" states are represented: -/// 1. Idle - await notification of MPD activity. -/// 2. All Non-Idle states: -/// 1. Playing - An active song is producing audio output. -/// 2. Paused - The current song is paused. -/// 3. Stopped - No song is actively playing. -/// 3. Disconnected - periodically attempt MPD (re-)connection. -/// -/// NOTE: Since this statechart is non-hierarchical, the above -/// states are flattened into a set. - -class State { - public: - virtual ~State() noexcept = default; - - virtual void entry() noexcept { spdlog::debug("mpd: ignore entry action"); } - virtual void exit() noexcept { spdlog::debug("mpd: ignore exit action"); } - - virtual void play() { spdlog::debug("mpd: ignore play state transition"); } - virtual void stop() { spdlog::debug("mpd: ignore stop state transition"); } - virtual void pause() { spdlog::debug("mpd: ignore pause state transition"); } - - /// Request state update the GUI. - virtual void update() noexcept { spdlog::debug("mpd: ignoring update method request"); } -}; - -class Idle : public State { - Context* const ctx_; - sigc::connection idle_connection_; - - public: - Idle(Context* const ctx) : ctx_{ctx} {} - virtual ~Idle() noexcept { this->exit(); }; - - void entry() noexcept override; - void exit() noexcept override; - - void play() override; - void stop() override; - void pause() override; - void update() noexcept override; - - private: - Idle(const Idle&) = delete; - Idle& operator=(const Idle&) = delete; - - bool on_io(Glib::IOCondition const&); -}; - -class Playing : public State { - Context* const ctx_; - sigc::connection timer_connection_; - - public: - Playing(Context* const ctx) : ctx_{ctx} {} - virtual ~Playing() noexcept { this->exit(); } - - void entry() noexcept override; - void exit() noexcept override; - - void pause() override; - void stop() override; - void update() noexcept override; - - private: - Playing(Playing const&) = delete; - Playing& operator=(Playing const&) = delete; - - bool on_timer(); -}; - -class Paused : public State { - Context* const ctx_; - sigc::connection timer_connection_; - - public: - Paused(Context* const ctx) : ctx_{ctx} {} - virtual ~Paused() noexcept { this->exit(); } - - void entry() noexcept override; - void exit() noexcept override; - - void play() override; - void stop() override; - void update() noexcept override; - - private: - Paused(Paused const&) = delete; - Paused& operator=(Paused const&) = delete; - - bool on_timer(); -}; - -class Stopped : public State { - Context* const ctx_; - sigc::connection timer_connection_; - - public: - Stopped(Context* const ctx) : ctx_{ctx} {} - virtual ~Stopped() noexcept { this->exit(); } - - void entry() noexcept override; - void exit() noexcept override; - - void play() override; - void pause() override; - void update() noexcept override; - - private: - Stopped(Stopped const&) = delete; - Stopped& operator=(Stopped const&) = delete; - - bool on_timer(); -}; - -class Disconnected : public State { - Context* const ctx_; - sigc::connection timer_connection_; - - public: - Disconnected(Context* const ctx) : ctx_{ctx} {} - virtual ~Disconnected() noexcept { this->exit(); } - - void entry() noexcept override; - void exit() noexcept override; - - void update() noexcept override; - - private: - Disconnected(Disconnected const&) = delete; - Disconnected& operator=(Disconnected const&) = delete; - - void arm_timer(int interval) noexcept; - void disarm_timer() noexcept; - - bool on_timer(); -}; - -class Context { - std::unique_ptr state_; - waybar::modules::MPD* mpd_module_; - - friend class State; - friend class Playing; - friend class Paused; - friend class Stopped; - friend class Disconnected; - friend class Idle; - - protected: - void setState(std::unique_ptr&& new_state) noexcept { - if (state_.get() != nullptr) { - state_->exit(); - } - state_ = std::move(new_state); - state_->entry(); - } - - bool is_connected() const; - bool is_playing() const; - bool is_paused() const; - bool is_stopped() const; - constexpr std::size_t interval() const; - void tryConnect() const; - void checkErrors(mpd_connection*) const; - void do_update(); - void queryMPD() const; - void fetchState() const; - constexpr mpd_state state() const; - void emit() const; - [[nodiscard]] unique_connection& connection(); - - public: - explicit Context(waybar::modules::MPD* const mpd_module) - : state_{std::make_unique(this)}, mpd_module_{mpd_module} { - state_->entry(); - } - - void play() { state_->play(); } - void stop() { state_->stop(); } - void pause() { state_->pause(); } - void update() noexcept { state_->update(); } -}; - -} // namespace waybar::modules::detail diff --git a/include/modules/mpd/state.inl.hpp b/include/modules/mpd/state.inl.hpp deleted file mode 100644 index 0d83b0b..0000000 --- a/include/modules/mpd/state.inl.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -namespace detail { - -inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; } -inline bool Context::is_playing() const { return mpd_module_->playing(); } -inline bool Context::is_paused() const { return mpd_module_->paused(); } -inline bool Context::is_stopped() const { return mpd_module_->stopped(); } - -constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_.count(); } -inline void Context::tryConnect() const { mpd_module_->tryConnect(); } -inline unique_connection& Context::connection() { return mpd_module_->connection_; } -constexpr inline mpd_state Context::state() const { return mpd_module_->state_; } - -inline void Context::do_update() { - mpd_module_->setLabel(); -} - -inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); } -inline void Context::queryMPD() const { mpd_module_->queryMPD(); } -inline void Context::fetchState() const { mpd_module_->fetchState(); } -inline void Context::emit() const { mpd_module_->emit(); } - -} // namespace detail diff --git a/meson.build b/meson.build index de20382..023894c 100644 --- a/meson.build +++ b/meson.build @@ -213,8 +213,7 @@ endif if libmpdclient.found() add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') - src_files += 'src/modules/mpd/mpd.cpp' - src_files += 'src/modules/mpd/state.cpp' + src_files += 'src/modules/mpd.cpp' endif if gtk_layer_shell.found() diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd.cpp similarity index 72% rename from src/modules/mpd/mpd.cpp rename to src/modules/mpd.cpp index 50f2817..26878b1 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd.cpp @@ -1,15 +1,8 @@ -#include "modules/mpd/mpd.hpp" +#include "modules/mpd.hpp" -#include +#include #include -#include "modules/mpd/state.hpp" -#if defined(MPD_NOINLINE) -namespace waybar::modules { -#include "modules/mpd/state.inl.hpp" -} // namespace waybar::modules -#endif - waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) : ALabel(config, "mpd", id, "{album} - {artist} - {title}", 5), module_name_(id.empty() ? "mpd" : "mpd#" + id), @@ -17,6 +10,7 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) 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()) { @@ -34,33 +28,73 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) 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 { - context_.update(); + 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(); // Call parent update ALabel::update(); } -void waybar::modules::MPD::queryMPD() { - if (connection_ != nullptr) { - spdlog::debug("{}: fetching state information", module_name_); - try { - fetchState(); - spdlog::debug("{}: fetch complete", module_name_); - } catch (std::exception const& e) { - spdlog::error("{}: {}", module_name_, e.what()); - state_ = MPD_STATE_UNKNOWN; +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) { + if (strcmp(e.what(), "Connection to MPD closed") == 0) { + spdlog::debug("{}: {}", module_name_, e.what()); + } else { + spdlog::warn("{}: {}", module_name_, e.what()); + } + } } - - dp.emit(); - } + }); } -std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) const { +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); @@ -99,7 +133,7 @@ void waybar::modules::MPD::setLabel() { auto format = format_; std::string artist, album_artist, album, title, date; - int song_pos, queue_length; + int song_pos, queue_length; std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; @@ -115,8 +149,8 @@ void waybar::modules::MPD::setLabel() { label_.get_style_context()->add_class("playing"); label_.get_style_context()->remove_class("paused"); } else if (paused()) { - format = config_["format-paused"].isString() ? config_["format-paused"].asString() - : config_["format"].asString(); + format = + config_["format-paused"].isString() ? config_["format-paused"].asString() : config_["format"].asString(); label_.get_style_context()->add_class("paused"); label_.get_style_context()->remove_class("playing"); } @@ -182,7 +216,7 @@ void waybar::modules::MPD::setLabel() { } } -std::string waybar::modules::MPD::getStateIcon() const { +std::string waybar::modules::MPD::getStateIcon() { if (!config_["state-icons"].isObject()) { return ""; } @@ -204,7 +238,7 @@ std::string waybar::modules::MPD::getStateIcon() const { } } -std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) const { +std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) { if (!config_[optionName + "-icons"].isObject()) { return ""; } @@ -227,11 +261,15 @@ void waybar::modules::MPD::tryConnect() { } connection_ = - detail::unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); + unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); - if (connection_ == nullptr) { + 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; } @@ -241,6 +279,7 @@ void waybar::modules::MPD::tryConnect() { } catch (std::runtime_error& e) { spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what()); connection_.reset(); + alternate_connection_.reset(); } } @@ -253,6 +292,7 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) { 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: @@ -266,20 +306,37 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) { } void waybar::modules::MPD::fetchState() { - if (connection_ == nullptr) { - spdlog::error("{}: Not connected to MPD", module_name_); - return; - } - auto conn = connection_.get(); - - status_ = detail::unique_status(mpd_run_status(conn), &mpd_status_free); + status_ = unique_status(mpd_run_status(conn), &mpd_status_free); checkErrors(conn); - state_ = mpd_status_get_state(status_.get()); checkErrors(conn); - song_ = detail::unique_song(mpd_run_current_song(conn), &mpd_song_free); + 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); } @@ -289,13 +346,24 @@ bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) { } if (e->button == 1) { - if (state_ == MPD_STATE_PLAY) - context_.pause(); - else - context_.play(); + 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) { - context_.stop(); + 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 || status_ == nullptr; +} + +bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; } + +bool waybar::modules::MPD::paused() { return connection_ != nullptr && state_ == MPD_STATE_PAUSE; } diff --git a/src/modules/mpd/state.cpp b/src/modules/mpd/state.cpp deleted file mode 100644 index 21b67ae..0000000 --- a/src/modules/mpd/state.cpp +++ /dev/null @@ -1,382 +0,0 @@ -#include "modules/mpd/state.hpp" - -#include -#include - -#include "modules/mpd/mpd.hpp" -#if defined(MPD_NOINLINE) -namespace waybar::modules { -#include "modules/mpd/state.inl.hpp" -} // namespace waybar::modules -#endif - -namespace waybar::modules::detail { - -#define IDLE_RUN_NOIDLE_AND_CMD(...) \ - if (idle_connection_.connected()) { \ - idle_connection_.disconnect(); \ - auto conn = ctx_->connection().get(); \ - if (!mpd_run_noidle(conn)) { \ - if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { \ - spdlog::error("mpd: Idle: failed to unregister for IDLE events"); \ - ctx_->checkErrors(conn); \ - } \ - } \ - __VA_ARGS__; \ - } - -void Idle::play() { - IDLE_RUN_NOIDLE_AND_CMD(mpd_run_play(conn)); - - ctx_->setState(std::make_unique(ctx_)); -} - -void Idle::pause() { - IDLE_RUN_NOIDLE_AND_CMD(mpd_run_pause(conn, true)); - - ctx_->setState(std::make_unique(ctx_)); -} - -void Idle::stop() { - IDLE_RUN_NOIDLE_AND_CMD(mpd_run_stop(conn)); - - ctx_->setState(std::make_unique(ctx_)); -} - -#undef IDLE_RUN_NOIDLE_AND_CMD - -void Idle::update() noexcept { - // This is intentionally blank. -} - -void Idle::entry() noexcept { - auto conn = ctx_->connection().get(); - assert(conn != nullptr); - - if (!mpd_send_idle_mask( - conn, static_cast(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) { - ctx_->checkErrors(conn); - spdlog::error("mpd: Idle: failed to register for IDLE events"); - } else { - spdlog::debug("mpd: Idle: watching FD"); - sigc::slot idle_slot = sigc::mem_fun(*this, &Idle::on_io); - idle_connection_ = - Glib::signal_io().connect(idle_slot, - mpd_connection_get_fd(conn), - Glib::IO_IN | Glib::IO_PRI | Glib::IO_ERR | Glib::IO_HUP); - } -} - -void Idle::exit() noexcept { - if (idle_connection_.connected()) { - idle_connection_.disconnect(); - spdlog::debug("mpd: Idle: unwatching FD"); - } -} - -bool Idle::on_io(Glib::IOCondition const&) { - auto conn = ctx_->connection().get(); - - // callback should do this: - enum mpd_idle events = mpd_recv_idle(conn, /* ignore_timeout?= */ false); - spdlog::debug("mpd: Idle: recv_idle events -> {}", events); - - mpd_response_finish(conn); - try { - ctx_->checkErrors(conn); - } catch (std::exception const& e) { - spdlog::warn("mpd: Idle: error: {}", e.what()); - ctx_->setState(std::make_unique(ctx_)); - return false; - } - - ctx_->fetchState(); - mpd_state state = ctx_->state(); - - if (state == MPD_STATE_STOP) { - ctx_->emit(); - ctx_->setState(std::make_unique(ctx_)); - } else if (state == MPD_STATE_PLAY) { - ctx_->emit(); - ctx_->setState(std::make_unique(ctx_)); - } else if (state == MPD_STATE_PAUSE) { - ctx_->emit(); - ctx_->setState(std::make_unique(ctx_)); - } else { - ctx_->emit(); - // self transition - ctx_->setState(std::make_unique(ctx_)); - } - - return false; -} - -void Playing::entry() noexcept { - sigc::slot timer_slot = sigc::mem_fun(*this, &Playing::on_timer); - timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 1'000); - spdlog::debug("mpd: Playing: enabled 1 second periodic timer."); -} - -void Playing::exit() noexcept { - if (timer_connection_.connected()) { - timer_connection_.disconnect(); - spdlog::debug("mpd: Playing: disabled 1 second periodic timer."); - } -} - -bool Playing::on_timer() { - // Attempt to connect with MPD. - try { - ctx_->tryConnect(); - - // Success? - if (!ctx_->is_connected()) { - ctx_->setState(std::make_unique(ctx_)); - return false; - } - - ctx_->fetchState(); - - if (!ctx_->is_playing()) { - if (ctx_->is_paused()) { - ctx_->setState(std::make_unique(ctx_)); - } else { - ctx_->setState(std::make_unique(ctx_)); - } - return false; - } - - ctx_->queryMPD(); - ctx_->emit(); - } catch (std::exception const& e) { - spdlog::warn("mpd: Playing: error: {}", e.what()); - ctx_->setState(std::make_unique(ctx_)); - return false; - } - - return true; -} - -void Playing::stop() { - if (timer_connection_.connected()) { - timer_connection_.disconnect(); - - mpd_run_stop(ctx_->connection().get()); - } - - ctx_->setState(std::make_unique(ctx_)); -} - -void Playing::pause() { - if (timer_connection_.connected()) { - timer_connection_.disconnect(); - - mpd_run_pause(ctx_->connection().get(), true); - } - - ctx_->setState(std::make_unique(ctx_)); -} - -void Playing::update() noexcept { ctx_->do_update(); } - -void Paused::entry() noexcept { - sigc::slot timer_slot = sigc::mem_fun(*this, &Paused::on_timer); - timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200); - spdlog::debug("mpd: Paused: enabled 200 ms periodic timer."); -} - -void Paused::exit() noexcept { - if (timer_connection_.connected()) { - timer_connection_.disconnect(); - spdlog::debug("mpd: Paused: disabled 200 ms periodic timer."); - } -} - -bool Paused::on_timer() { - bool rc = true; - - // Attempt to connect with MPD. - try { - ctx_->tryConnect(); - - // Success? - if (!ctx_->is_connected()) { - ctx_->setState(std::make_unique(ctx_)); - return false; - } - - ctx_->fetchState(); - - ctx_->emit(); - - if (ctx_->is_paused()) { - ctx_->setState(std::make_unique(ctx_)); - rc = false; - } else if (ctx_->is_playing()) { - ctx_->setState(std::make_unique(ctx_)); - rc = false; - } else if (ctx_->is_stopped()) { - ctx_->setState(std::make_unique(ctx_)); - rc = false; - } - } catch (std::exception const& e) { - spdlog::warn("mpd: Paused: error: {}", e.what()); - ctx_->setState(std::make_unique(ctx_)); - rc = false; - } - - return rc; -} - -void Paused::play() { - if (timer_connection_.connected()) { - timer_connection_.disconnect(); - - mpd_run_play(ctx_->connection().get()); - } - - ctx_->setState(std::make_unique(ctx_)); -} - -void Paused::stop() { - if (timer_connection_.connected()) { - timer_connection_.disconnect(); - - mpd_run_stop(ctx_->connection().get()); - } - - ctx_->setState(std::make_unique(ctx_)); -} - -void Paused::update() noexcept { ctx_->do_update(); } - -void Stopped::entry() noexcept { - sigc::slot timer_slot = sigc::mem_fun(*this, &Stopped::on_timer); - timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200); - spdlog::debug("mpd: Stopped: enabled 200 ms periodic timer."); -} - -void Stopped::exit() noexcept { - if (timer_connection_.connected()) { - timer_connection_.disconnect(); - spdlog::debug("mpd: Stopped: disabled 200 ms periodic timer."); - } -} - -bool Stopped::on_timer() { - bool rc = true; - - // Attempt to connect with MPD. - try { - ctx_->tryConnect(); - - // Success? - if (!ctx_->is_connected()) { - ctx_->setState(std::make_unique(ctx_)); - return false; - } - - ctx_->fetchState(); - - ctx_->emit(); - - if (ctx_->is_stopped()) { - ctx_->setState(std::make_unique(ctx_)); - rc = false; - } else if (ctx_->is_playing()) { - ctx_->setState(std::make_unique(ctx_)); - rc = false; - } else if (ctx_->is_paused()) { - ctx_->setState(std::make_unique(ctx_)); - rc = false; - } - } catch (std::exception const& e) { - spdlog::warn("mpd: Stopped: error: {}", e.what()); - ctx_->setState(std::make_unique(ctx_)); - rc = false; - } - - return rc; -} - -void Stopped::play() { - if (timer_connection_.connected()) { - timer_connection_.disconnect(); - - mpd_run_play(ctx_->connection().get()); - } - - ctx_->setState(std::make_unique(ctx_)); -} - -void Stopped::pause() { - if (timer_connection_.connected()) { - timer_connection_.disconnect(); - - mpd_run_pause(ctx_->connection().get(), true); - } - - ctx_->setState(std::make_unique(ctx_)); -} - -void Stopped::update() noexcept { ctx_->do_update(); } - -void Disconnected::arm_timer(int interval) noexcept { - // unregister timer, if present - disarm_timer(); - - // register timer - sigc::slot timer_slot = sigc::mem_fun(*this, &Disconnected::on_timer); - timer_connection_ = - Glib::signal_timeout().connect(timer_slot, interval); - spdlog::debug("mpd: Disconnected: enabled interval timer."); -} - -void Disconnected::disarm_timer() noexcept { - // unregister timer, if present - if (timer_connection_.connected()) { - timer_connection_.disconnect(); - spdlog::debug("mpd: Disconnected: disabled interval timer."); - } -} - -void Disconnected::entry() noexcept { - arm_timer(1'000); -} - -void Disconnected::exit() noexcept { - disarm_timer(); -} - -bool Disconnected::on_timer() { - // Attempt to connect with MPD. - try { - ctx_->tryConnect(); - - // Success? - if (ctx_->is_connected()) { - ctx_->fetchState(); - ctx_->emit(); - - if (ctx_->is_playing()) { - ctx_->setState(std::make_unique(ctx_)); - } else if (ctx_->is_paused()) { - ctx_->setState(std::make_unique(ctx_)); - } else { - ctx_->setState(std::make_unique(ctx_)); - } - - return false; // do not rearm timer - } - } catch (std::exception const& e) { - spdlog::warn("mpd: Disconnected: error: {}", e.what()); - } - - arm_timer(ctx_->interval() * 1'000); - - return false; -} - -void Disconnected::update() noexcept { ctx_->do_update(); } - -} // namespace waybar::modules::detail From 8f961ac3976048d7a389d77737d9694f26fe863d Mon Sep 17 00:00:00 2001 From: Joseph Benden Date: Sat, 3 Oct 2020 22:01:51 -0700 Subject: [PATCH 093/355] mpd: revamped to event-driven, single-threaded Fix MPD connection issues by converting/rewriting module into a state-machine driven system. It is fully single-threaded and uses events for transitioning between states. It supports all features and functionality of the previous MPD module. Signed-off-by: Joseph Benden --- include/factory.hpp | 2 +- include/modules/mpd.hpp | 74 ------ include/modules/mpd/mpd.hpp | 66 ++++++ include/modules/mpd/state.hpp | 217 +++++++++++++++++ include/modules/mpd/state.inl.hpp | 24 ++ meson.build | 3 +- src/modules/{ => mpd}/mpd.cpp | 154 ++++-------- src/modules/mpd/state.cpp | 382 ++++++++++++++++++++++++++++++ 8 files changed, 735 insertions(+), 187 deletions(-) delete mode 100644 include/modules/mpd.hpp create mode 100644 include/modules/mpd/mpd.hpp create mode 100644 include/modules/mpd/state.hpp create mode 100644 include/modules/mpd/state.inl.hpp rename src/modules/{ => mpd}/mpd.cpp (73%) create mode 100644 src/modules/mpd/state.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 3efe6cb..f73c6e1 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -37,7 +37,7 @@ #include "modules/pulseaudio.hpp" #endif #ifdef HAVE_LIBMPDCLIENT -#include "modules/mpd.hpp" +#include "modules/mpd/mpd.hpp" #endif #ifdef HAVE_LIBSNDIO #include "modules/sndio.hpp" diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp deleted file mode 100644 index d08b28b..0000000 --- a/include/modules/mpd.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#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(); - bool paused(); - - 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 diff --git a/include/modules/mpd/mpd.hpp b/include/modules/mpd/mpd.hpp new file mode 100644 index 0000000..f92df93 --- /dev/null +++ b/include/modules/mpd/mpd.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "ALabel.hpp" +#include "modules/mpd/state.hpp" + +namespace waybar::modules { + +class MPD : public ALabel { + friend class detail::Context; + + // State machine + detail::Context context_{this}; + + const std::string module_name_; + + // Not using unique_ptr since we don't manage the pointer + // (It's either nullptr, or from the config) + const char* server_; + const unsigned port_; + + unsigned timeout_; + + detail::unique_connection connection_; + + detail::unique_status status_; + mpd_state state_; + detail::unique_song song_; + + public: + MPD(const std::string&, const Json::Value&); + virtual ~MPD() noexcept = default; + auto update() -> void; + + private: + std::string getTag(mpd_tag_type type, unsigned idx = 0) const; + void setLabel(); + std::string getStateIcon() const; + std::string getOptionIcon(std::string optionName, bool activated) const; + + // GUI-side methods + bool handlePlayPause(GdkEventButton* const&); + void emit() { dp.emit(); } + + // MPD-side, Non-GUI methods. + void tryConnect(); + void checkErrors(mpd_connection* conn); + void fetchState(); + void queryMPD(); + + inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; } + inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; } + inline bool paused() const { return connection_ && state_ == MPD_STATE_PAUSE; } +}; + +#if !defined(MPD_NOINLINE) +#include "modules/mpd/state.inl.hpp" +#endif + +} // namespace waybar::modules diff --git a/include/modules/mpd/state.hpp b/include/modules/mpd/state.hpp new file mode 100644 index 0000000..3b18159 --- /dev/null +++ b/include/modules/mpd/state.hpp @@ -0,0 +1,217 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "ALabel.hpp" + +namespace waybar::modules { +class MPD; +} // namespace waybar::modules + +namespace waybar::modules::detail { + +using unique_connection = std::unique_ptr; +using unique_status = std::unique_ptr; +using unique_song = std::unique_ptr; + +class Context; + +/// This state machine loosely follows a non-hierarchical, statechart +/// pattern, and includes ENTRY and EXIT actions. +/// +/// The State class is the base class for all other states. The +/// entry and exit methods are automatically called when entering +/// into a new state and exiting from the current state. This +/// includes initially entering (Disconnected class) and exiting +/// Waybar. +/// +/// The following nested "top-level" states are represented: +/// 1. Idle - await notification of MPD activity. +/// 2. All Non-Idle states: +/// 1. Playing - An active song is producing audio output. +/// 2. Paused - The current song is paused. +/// 3. Stopped - No song is actively playing. +/// 3. Disconnected - periodically attempt MPD (re-)connection. +/// +/// NOTE: Since this statechart is non-hierarchical, the above +/// states are flattened into a set. + +class State { + public: + virtual ~State() noexcept = default; + + virtual void entry() noexcept { spdlog::debug("mpd: ignore entry action"); } + virtual void exit() noexcept { spdlog::debug("mpd: ignore exit action"); } + + virtual void play() { spdlog::debug("mpd: ignore play state transition"); } + virtual void stop() { spdlog::debug("mpd: ignore stop state transition"); } + virtual void pause() { spdlog::debug("mpd: ignore pause state transition"); } + + /// Request state update the GUI. + virtual void update() noexcept { spdlog::debug("mpd: ignoring update method request"); } +}; + +class Idle : public State { + Context* const ctx_; + sigc::connection idle_connection_; + + public: + Idle(Context* const ctx) : ctx_{ctx} {} + virtual ~Idle() noexcept { this->exit(); }; + + void entry() noexcept override; + void exit() noexcept override; + + void play() override; + void stop() override; + void pause() override; + void update() noexcept override; + + private: + Idle(const Idle&) = delete; + Idle& operator=(const Idle&) = delete; + + bool on_io(Glib::IOCondition const&); +}; + +class Playing : public State { + Context* const ctx_; + sigc::connection timer_connection_; + + public: + Playing(Context* const ctx) : ctx_{ctx} {} + virtual ~Playing() noexcept { this->exit(); } + + void entry() noexcept override; + void exit() noexcept override; + + void pause() override; + void stop() override; + void update() noexcept override; + + private: + Playing(Playing const&) = delete; + Playing& operator=(Playing const&) = delete; + + bool on_timer(); +}; + +class Paused : public State { + Context* const ctx_; + sigc::connection timer_connection_; + + public: + Paused(Context* const ctx) : ctx_{ctx} {} + virtual ~Paused() noexcept { this->exit(); } + + void entry() noexcept override; + void exit() noexcept override; + + void play() override; + void stop() override; + void update() noexcept override; + + private: + Paused(Paused const&) = delete; + Paused& operator=(Paused const&) = delete; + + bool on_timer(); +}; + +class Stopped : public State { + Context* const ctx_; + sigc::connection timer_connection_; + + public: + Stopped(Context* const ctx) : ctx_{ctx} {} + virtual ~Stopped() noexcept { this->exit(); } + + void entry() noexcept override; + void exit() noexcept override; + + void play() override; + void pause() override; + void update() noexcept override; + + private: + Stopped(Stopped const&) = delete; + Stopped& operator=(Stopped const&) = delete; + + bool on_timer(); +}; + +class Disconnected : public State { + Context* const ctx_; + sigc::connection timer_connection_; + + public: + Disconnected(Context* const ctx) : ctx_{ctx} {} + virtual ~Disconnected() noexcept { this->exit(); } + + void entry() noexcept override; + void exit() noexcept override; + + void update() noexcept override; + + private: + Disconnected(Disconnected const&) = delete; + Disconnected& operator=(Disconnected const&) = delete; + + void arm_timer(int interval) noexcept; + void disarm_timer() noexcept; + + bool on_timer(); +}; + +class Context { + std::unique_ptr state_; + waybar::modules::MPD* mpd_module_; + + friend class State; + friend class Playing; + friend class Paused; + friend class Stopped; + friend class Disconnected; + friend class Idle; + + protected: + void setState(std::unique_ptr&& new_state) noexcept { + if (state_.get() != nullptr) { + state_->exit(); + } + state_ = std::move(new_state); + state_->entry(); + } + + bool is_connected() const; + bool is_playing() const; + bool is_paused() const; + bool is_stopped() const; + constexpr std::size_t interval() const; + void tryConnect() const; + void checkErrors(mpd_connection*) const; + void do_update(); + void queryMPD() const; + void fetchState() const; + constexpr mpd_state state() const; + void emit() const; + [[nodiscard]] unique_connection& connection(); + + public: + explicit Context(waybar::modules::MPD* const mpd_module) + : state_{std::make_unique(this)}, mpd_module_{mpd_module} { + state_->entry(); + } + + void play() { state_->play(); } + void stop() { state_->stop(); } + void pause() { state_->pause(); } + void update() noexcept { state_->update(); } +}; + +} // namespace waybar::modules::detail diff --git a/include/modules/mpd/state.inl.hpp b/include/modules/mpd/state.inl.hpp new file mode 100644 index 0000000..0d83b0b --- /dev/null +++ b/include/modules/mpd/state.inl.hpp @@ -0,0 +1,24 @@ +#pragma once + +namespace detail { + +inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; } +inline bool Context::is_playing() const { return mpd_module_->playing(); } +inline bool Context::is_paused() const { return mpd_module_->paused(); } +inline bool Context::is_stopped() const { return mpd_module_->stopped(); } + +constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_.count(); } +inline void Context::tryConnect() const { mpd_module_->tryConnect(); } +inline unique_connection& Context::connection() { return mpd_module_->connection_; } +constexpr inline mpd_state Context::state() const { return mpd_module_->state_; } + +inline void Context::do_update() { + mpd_module_->setLabel(); +} + +inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); } +inline void Context::queryMPD() const { mpd_module_->queryMPD(); } +inline void Context::fetchState() const { mpd_module_->fetchState(); } +inline void Context::emit() const { mpd_module_->emit(); } + +} // namespace detail diff --git a/meson.build b/meson.build index 023894c..de20382 100644 --- a/meson.build +++ b/meson.build @@ -213,7 +213,8 @@ endif if libmpdclient.found() add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') - src_files += 'src/modules/mpd.cpp' + src_files += 'src/modules/mpd/mpd.cpp' + src_files += 'src/modules/mpd/state.cpp' endif if gtk_layer_shell.found() diff --git a/src/modules/mpd.cpp b/src/modules/mpd/mpd.cpp similarity index 73% rename from src/modules/mpd.cpp rename to src/modules/mpd/mpd.cpp index 26878b1..edce941 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -1,8 +1,15 @@ -#include "modules/mpd.hpp" +#include "modules/mpd/mpd.hpp" #include #include +#include "modules/mpd/state.hpp" +#if defined(MPD_NOINLINE) +namespace waybar::modules { +#include "modules/mpd/state.inl.hpp" +} // namespace waybar::modules +#endif + waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) : ALabel(config, "mpd", id, "{album} - {artist} - {title}", 5), module_name_(id.empty() ? "mpd" : "mpd#" + id), @@ -10,7 +17,6 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) 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()) { @@ -28,73 +34,33 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) 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(); + context_.update(); // Call parent update ALabel::update(); } -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) { - if (strcmp(e.what(), "Connection to MPD closed") == 0) { - spdlog::debug("{}: {}", module_name_, e.what()); - } else { - spdlog::warn("{}: {}", module_name_, e.what()); - } - } +void waybar::modules::MPD::queryMPD() { + if (connection_ != nullptr) { + spdlog::debug("{}: fetching state information", module_name_); + try { + fetchState(); + spdlog::debug("{}: fetch complete", module_name_); + } catch (std::exception const& e) { + spdlog::error("{}: {}", module_name_, e.what()); + state_ = MPD_STATE_UNKNOWN; } - }); + + dp.emit(); + } } -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 waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) const { std::string result = config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A"; const char* tag = mpd_song_get_tag(song_.get(), type, idx); @@ -133,7 +99,7 @@ void waybar::modules::MPD::setLabel() { auto format = format_; std::string artist, album_artist, album, title, date; - int song_pos, queue_length; + int song_pos, queue_length; std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; @@ -149,8 +115,8 @@ void waybar::modules::MPD::setLabel() { label_.get_style_context()->add_class("playing"); label_.get_style_context()->remove_class("paused"); } else if (paused()) { - format = - config_["format-paused"].isString() ? config_["format-paused"].asString() : config_["format"].asString(); + format = config_["format-paused"].isString() ? config_["format-paused"].asString() + : config_["format"].asString(); label_.get_style_context()->add_class("paused"); label_.get_style_context()->remove_class("playing"); } @@ -216,7 +182,7 @@ void waybar::modules::MPD::setLabel() { } } -std::string waybar::modules::MPD::getStateIcon() { +std::string waybar::modules::MPD::getStateIcon() const { if (!config_["state-icons"].isObject()) { return ""; } @@ -238,7 +204,7 @@ std::string waybar::modules::MPD::getStateIcon() { } } -std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) { +std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) const { if (!config_[optionName + "-icons"].isObject()) { return ""; } @@ -261,15 +227,11 @@ void waybar::modules::MPD::tryConnect() { } connection_ = - unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); + detail::unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); - alternate_connection_ = - unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free); - - if (connection_ == nullptr || alternate_connection_ == nullptr) { + if (connection_ == nullptr) { spdlog::error("{}: Failed to connect to MPD", module_name_); connection_.reset(); - alternate_connection_.reset(); return; } @@ -279,7 +241,6 @@ void waybar::modules::MPD::tryConnect() { } catch (std::runtime_error& e) { spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what()); connection_.reset(); - alternate_connection_.reset(); } } @@ -292,7 +253,6 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) { 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: @@ -306,37 +266,20 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) { } void waybar::modules::MPD::fetchState() { + if (connection_ == nullptr) { + spdlog::error("{}: Not connected to MPD", module_name_); + return; + } + auto conn = connection_.get(); - status_ = unique_status(mpd_run_status(conn), &mpd_status_free); + + status_ = detail::unique_status(mpd_run_status(conn), &mpd_status_free); checkErrors(conn); + 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); - + song_ = detail::unique_song(mpd_run_current_song(conn), &mpd_song_free); checkErrors(conn); } @@ -346,24 +289,13 @@ bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) { } if (e->button == 1) { - std::lock_guard guard(connection_lock_); - if (stopped()) { - mpd_run_play(connection_.get()); - } else { - mpd_run_toggle_pause(connection_.get()); - } + if (state_ == MPD_STATE_PLAY) + context_.pause(); + else + context_.play(); } else if (e->button == 3) { - std::lock_guard guard(connection_lock_); - mpd_run_stop(connection_.get()); + context_.stop(); } return true; } - -bool waybar::modules::MPD::stopped() { - return connection_ == nullptr || state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP || status_ == nullptr; -} - -bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; } - -bool waybar::modules::MPD::paused() { return connection_ != nullptr && state_ == MPD_STATE_PAUSE; } diff --git a/src/modules/mpd/state.cpp b/src/modules/mpd/state.cpp new file mode 100644 index 0000000..570c167 --- /dev/null +++ b/src/modules/mpd/state.cpp @@ -0,0 +1,382 @@ +#include "modules/mpd/state.hpp" + +#include +#include + +#include "modules/mpd/mpd.hpp" +#if defined(MPD_NOINLINE) +namespace waybar::modules { +#include "modules/mpd/state.inl.hpp" +} // namespace waybar::modules +#endif + +namespace waybar::modules::detail { + +#define IDLE_RUN_NOIDLE_AND_CMD(...) \ + if (idle_connection_.connected()) { \ + idle_connection_.disconnect(); \ + auto conn = ctx_->connection().get(); \ + if (!mpd_run_noidle(conn)) { \ + if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { \ + spdlog::error("mpd: Idle: failed to unregister for IDLE events"); \ + ctx_->checkErrors(conn); \ + } \ + } \ + __VA_ARGS__; \ + } + +void Idle::play() { + IDLE_RUN_NOIDLE_AND_CMD(mpd_run_play(conn)); + + ctx_->setState(std::make_unique(ctx_)); +} + +void Idle::pause() { + IDLE_RUN_NOIDLE_AND_CMD(mpd_run_pause(conn, true)); + + ctx_->setState(std::make_unique(ctx_)); +} + +void Idle::stop() { + IDLE_RUN_NOIDLE_AND_CMD(mpd_run_stop(conn)); + + ctx_->setState(std::make_unique(ctx_)); +} + +#undef IDLE_RUN_NOIDLE_AND_CMD + +void Idle::update() noexcept { + // This is intentionally blank. +} + +void Idle::entry() noexcept { + auto conn = ctx_->connection().get(); + assert(conn != nullptr); + + if (!mpd_send_idle_mask( + conn, static_cast(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) { + ctx_->checkErrors(conn); + spdlog::error("mpd: Idle: failed to register for IDLE events"); + } else { + spdlog::debug("mpd: Idle: watching FD"); + sigc::slot idle_slot = sigc::mem_fun(*this, &Idle::on_io); + idle_connection_ = + Glib::signal_io().connect(idle_slot, + mpd_connection_get_fd(conn), + Glib::IO_IN | Glib::IO_PRI | Glib::IO_ERR | Glib::IO_HUP); + } +} + +void Idle::exit() noexcept { + if (idle_connection_.connected()) { + idle_connection_.disconnect(); + spdlog::debug("mpd: Idle: unwatching FD"); + } +} + +bool Idle::on_io(Glib::IOCondition const&) { + auto conn = ctx_->connection().get(); + + // callback should do this: + enum mpd_idle events = mpd_recv_idle(conn, /* ignore_timeout?= */ false); + spdlog::debug("mpd: Idle: recv_idle events -> {}", events); + + mpd_response_finish(conn); + try { + ctx_->checkErrors(conn); + } catch (std::exception const& e) { + spdlog::warn("mpd: Idle: error: {}", e.what()); + ctx_->setState(std::make_unique(ctx_)); + return false; + } + + ctx_->fetchState(); + mpd_state state = ctx_->state(); + + if (state == MPD_STATE_STOP) { + ctx_->emit(); + ctx_->setState(std::make_unique(ctx_)); + } else if (state == MPD_STATE_PLAY) { + ctx_->emit(); + ctx_->setState(std::make_unique(ctx_)); + } else if (state == MPD_STATE_PAUSE) { + ctx_->emit(); + ctx_->setState(std::make_unique(ctx_)); + } else { + ctx_->emit(); + // self transition + ctx_->setState(std::make_unique(ctx_)); + } + + return false; +} + +void Playing::entry() noexcept { + sigc::slot timer_slot = sigc::mem_fun(*this, &Playing::on_timer); + timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 1'000); + spdlog::debug("mpd: Playing: enabled 1 second periodic timer."); +} + +void Playing::exit() noexcept { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + spdlog::debug("mpd: Playing: disabled 1 second periodic timer."); + } +} + +bool Playing::on_timer() { + // Attempt to connect with MPD. + try { + ctx_->tryConnect(); + + // Success? + if (!ctx_->is_connected()) { + ctx_->setState(std::make_unique(ctx_)); + return false; + } + + ctx_->fetchState(); + + if (!ctx_->is_playing()) { + if (ctx_->is_paused()) { + ctx_->setState(std::make_unique(ctx_)); + } else { + ctx_->setState(std::make_unique(ctx_)); + } + return false; + } + + ctx_->queryMPD(); + ctx_->emit(); + } catch (std::exception const& e) { + spdlog::warn("mpd: Playing: error: {}", e.what()); + ctx_->setState(std::make_unique(ctx_)); + return false; + } + + return true; +} + +void Playing::stop() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_stop(ctx_->connection().get()); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Playing::pause() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_pause(ctx_->connection().get(), true); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Playing::update() noexcept { ctx_->do_update(); } + +void Paused::entry() noexcept { + sigc::slot timer_slot = sigc::mem_fun(*this, &Paused::on_timer); + timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200); + spdlog::debug("mpd: Paused: enabled 200 ms periodic timer."); +} + +void Paused::exit() noexcept { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + spdlog::debug("mpd: Paused: disabled 200 ms periodic timer."); + } +} + +bool Paused::on_timer() { + bool rc = true; + + // Attempt to connect with MPD. + try { + ctx_->tryConnect(); + + // Success? + if (!ctx_->is_connected()) { + ctx_->setState(std::make_unique(ctx_)); + return false; + } + + ctx_->fetchState(); + + ctx_->emit(); + + if (ctx_->is_paused()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } else if (ctx_->is_playing()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } else if (ctx_->is_stopped()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } + } catch (std::exception const& e) { + spdlog::warn("mpd: Paused: error: {}", e.what()); + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } + + return rc; +} + +void Paused::play() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_play(ctx_->connection().get()); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Paused::stop() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_stop(ctx_->connection().get()); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Paused::update() noexcept { ctx_->do_update(); } + +void Stopped::entry() noexcept { + sigc::slot timer_slot = sigc::mem_fun(*this, &Stopped::on_timer); + timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200); + spdlog::debug("mpd: Stopped: enabled 200 ms periodic timer."); +} + +void Stopped::exit() noexcept { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + spdlog::debug("mpd: Stopped: disabled 200 ms periodic timer."); + } +} + +bool Stopped::on_timer() { + bool rc = true; + + // Attempt to connect with MPD. + try { + ctx_->tryConnect(); + + // Success? + if (!ctx_->is_connected()) { + ctx_->setState(std::make_unique(ctx_)); + return false; + } + + ctx_->fetchState(); + + ctx_->emit(); + + if (ctx_->is_stopped()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } else if (ctx_->is_playing()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } else if (ctx_->is_paused()) { + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } + } catch (std::exception const& e) { + spdlog::warn("mpd: Stopped: error: {}", e.what()); + ctx_->setState(std::make_unique(ctx_)); + rc = false; + } + + return rc; +} + +void Stopped::play() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_play(ctx_->connection().get()); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Stopped::pause() { + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + + mpd_run_pause(ctx_->connection().get(), true); + } + + ctx_->setState(std::make_unique(ctx_)); +} + +void Stopped::update() noexcept { ctx_->do_update(); } + +void Disconnected::arm_timer(int interval) noexcept { + // unregister timer, if present + disarm_timer(); + + // register timer + sigc::slot timer_slot = sigc::mem_fun(*this, &Disconnected::on_timer); + timer_connection_ = + Glib::signal_timeout().connect(timer_slot, interval); + spdlog::debug("mpd: Disconnected: enabled interval timer."); +} + +void Disconnected::disarm_timer() noexcept { + // unregister timer, if present + if (timer_connection_.connected()) { + timer_connection_.disconnect(); + spdlog::debug("mpd: Disconnected: disabled interval timer."); + } +} + +void Disconnected::entry() noexcept { + arm_timer(1'000); +} + +void Disconnected::exit() noexcept { + disarm_timer(); +} + +bool Disconnected::on_timer() { + // Attempt to connect with MPD. + try { + ctx_->tryConnect(); + + // Success? + if (ctx_->is_connected()) { + ctx_->fetchState(); + ctx_->emit(); + + if (ctx_->is_playing()) { + ctx_->setState(std::make_unique(ctx_)); + } else if (ctx_->is_paused()) { + ctx_->setState(std::make_unique(ctx_)); + } else { + ctx_->setState(std::make_unique(ctx_)); + } + + return false; // do not rearm timer + } + } catch (std::exception const& e) { + spdlog::warn("mpd: Disconnected: error: {}", e.what()); + } + + arm_timer(ctx_->interval() * 1'000); + + return false; +} + +void Disconnected::update() noexcept { ctx_->do_update(); } + +} // namespace waybar::modules::detail From 587eb5fdb4d7de2891fa0a874f7c4ea0c9d4d882 Mon Sep 17 00:00:00 2001 From: Joseph Benden Date: Mon, 19 Oct 2020 11:54:36 -0700 Subject: [PATCH 094/355] mpd: support password protected MPD - Add MPD module option `password`, and document it. - Add logic to send the password, directly after connecting to MPD. Fixes: #576 Signed-off-by: Joseph Benden --- include/modules/mpd/mpd.hpp | 7 ++++--- man/waybar-mpd.5.scd | 4 ++++ src/modules/mpd/mpd.cpp | 11 +++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/include/modules/mpd/mpd.hpp b/include/modules/mpd/mpd.hpp index f92df93..0fc1ce9 100644 --- a/include/modules/mpd/mpd.hpp +++ b/include/modules/mpd/mpd.hpp @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include @@ -22,8 +22,9 @@ class MPD : public ALabel { // Not using unique_ptr since we don't manage the pointer // (It's either nullptr, or from the config) - const char* server_; - const unsigned port_; + const char* server_; + const unsigned port_; + const std::string password_; unsigned timeout_; diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index e8105de..8c33c62 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -20,6 +20,10 @@ Addressed by *mpd* typeof: integer ++ The port MPD listens to. If empty, use the default port. +*password*: ++ + typeof: string ++ + The password required to connect to the MPD server. If empty, no password is sent to MPD. + *interval*: ++ typeof: integer++ default: 5 ++ diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index edce941..6dc24e7 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -15,6 +15,7 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) module_name_(id.empty() ? "mpd" : "mpd#" + id), server_(nullptr), port_(config_["port"].isUInt() ? config["port"].asUInt() : 0), + password_(config_["password"].empty() ? "" : config_["password"].asString()), timeout_(config_["timeout"].isUInt() ? config_["timeout"].asUInt() * 1'000 : 30'000), connection_(nullptr, &mpd_connection_free), status_(nullptr, &mpd_status_free), @@ -238,6 +239,16 @@ void waybar::modules::MPD::tryConnect() { try { checkErrors(connection_.get()); spdlog::debug("{}: Connected to MPD", module_name_); + + if (!password_.empty()) { + bool res = mpd_run_password(connection_.get(), password_.c_str()); + if (!res) { + spdlog::error("{}: Wrong MPD password", module_name_); + connection_.reset(); + return; + } + checkErrors(connection_.get()); + } } catch (std::runtime_error& e) { spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what()); connection_.reset(); From ed402d7583291eda34cac4a32b4caac58e7dc900 Mon Sep 17 00:00:00 2001 From: nikto_b Date: Tue, 20 Oct 2020 12:20:58 +0300 Subject: [PATCH 095/355] feature: language submodule - created man page --- man/waybar-sway-language.5.scd | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 man/waybar-sway-language.5.scd diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd new file mode 100644 index 0000000..a288cca --- /dev/null +++ b/man/waybar-sway-language.5.scd @@ -0,0 +1,72 @@ +waybar-sway-language(5) + +# NAME + +waybar - sway language module + +# DESCRIPTION + +The *language* module displays the current keyboard layout in Sway + +# CONFIGURATION + +Addressed by *sway/language* + +*format*: ++ + typeof: string ++ + default: {} ++ + The format, how information should be displayed. On {} data gets inserted. + +*rotate*: ++ + typeof: integer ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*on-click*: ++ + typeof: string ++ + Command to execute when clicked on the module. + +*on-click-middle*: ++ + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. + +*on-click-right*: ++ + typeof: string ++ + Command to execute when you right clicked on the module. + +*on-update*: ++ + typeof: string ++ + Command to execute when the module is updated. + +*on-scroll-up*: ++ + typeof: string ++ + Command to execute when scrolling up on the module. + +*on-scroll-down*: ++ + typeof: string ++ + Command to execute when scrolling down on the module. + +*smooth-scrolling-threshold*: ++ + typeof: double ++ + Threshold to be used when scrolling. + +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to disable tooltip on hover. + +# EXAMPLES + +``` +"sway/language": { + "format": "{}", + "max-length": 50 +} +``` + +# STYLE + +- *#language* From be3f47b37437e11a84031b076ef46802c654c7fe Mon Sep 17 00:00:00 2001 From: Flakebi Date: Fri, 23 Oct 2020 21:13:20 +0200 Subject: [PATCH 096/355] Fix various mpd bugs - Add elapsedTime and totalTime to tooltip format arguments - Catch format exceptions and print error - Copy mpd connection error message before it gets freed - Update display after connection to mpd was lost --- src/modules/mpd/mpd.cpp | 74 ++++++++++++++++++++++----------------- src/modules/mpd/state.cpp | 1 + 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 6dc24e7..710f23f 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -144,42 +144,51 @@ void waybar::modules::MPD::setLabel() { 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("songPosition", song_pos), - fmt::arg("queueLength", queue_length), - fmt::arg("stateIcon", stateIcon), - fmt::arg("consumeIcon", consumeIcon), - fmt::arg("randomIcon", randomIcon), - fmt::arg("repeatIcon", repeatIcon), - fmt::arg("singleIcon", singleIcon))); + try { + 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("songPosition", song_pos), + fmt::arg("queueLength", queue_length), + fmt::arg("stateIcon", stateIcon), + fmt::arg("consumeIcon", consumeIcon), + fmt::arg("randomIcon", randomIcon), + fmt::arg("repeatIcon", repeatIcon), + fmt::arg("singleIcon", singleIcon))); + } catch (fmt::format_error const& e) { + spdlog::warn("mpd: format error: {}", e.what()); + } 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("songPosition", song_pos), - fmt::arg("queueLength", queue_length), - 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); + try { + 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("elapsedTime", elapsedTime), + fmt::arg("totalTime", totalTime), + fmt::arg("songPosition", song_pos), + fmt::arg("queueLength", queue_length), + 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); + } catch (fmt::format_error const& e) { + spdlog::warn("mpd: format error (tooltip): {}", e.what()); + } } } @@ -269,8 +278,9 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) { default: if (conn) { auto error_message = mpd_connection_get_error_message(conn); + std::string error(error_message); mpd_connection_clear_error(conn); - throw std::runtime_error(std::string(error_message)); + throw std::runtime_error(error); } throw std::runtime_error("Invalid connection"); } diff --git a/src/modules/mpd/state.cpp b/src/modules/mpd/state.cpp index 570c167..ffe18e7 100644 --- a/src/modules/mpd/state.cpp +++ b/src/modules/mpd/state.cpp @@ -341,6 +341,7 @@ void Disconnected::disarm_timer() noexcept { } void Disconnected::entry() noexcept { + ctx_->emit(); arm_timer(1'000); } From 67d54ef3d53e5257e465ec556177ea569100263e Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 24 Oct 2020 22:56:24 -0700 Subject: [PATCH 097/355] fix(wlr/taskbar): do not bind to unsupported protocol versions It's not allowed to bind to a higher version of a wayland protocol than supported by the client. Binding wlr-foreign-toplevel-manager-v1 v3 to a generated code for v2 causes errors in libwayland due to a missing handler for `zwlr_foreign_toplevel_handle_v1.parent` event. --- src/client.cpp | 2 ++ src/modules/wlr/taskbar.cpp | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/client.cpp b/src/client.cpp index 70cc22c..9dd93d8 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -32,6 +32,8 @@ void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint const char *interface, uint32_t version) { auto client = static_cast(data); if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + // limit version to a highest supported by the client protocol file + version = std::min(version, zwlr_layer_shell_v1_interface.version); 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 && diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 45915dc..8d6fa9b 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -653,9 +653,11 @@ void Taskbar::register_manager(struct wl_registry *registry, uint32_t name, uint spdlog::warn("Register foreign toplevel manager again although already existing!"); return; } - if (version != 2) { + if (version < ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION) { spdlog::warn("Using different foreign toplevel manager protocol version: {}", version); } + // limit version to a highest supported by the client protocol file + version = std::min(version, zwlr_foreign_toplevel_manager_v1_interface.version); manager_ = static_cast(wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, version)); @@ -672,6 +674,7 @@ void Taskbar::register_seat(struct wl_registry *registry, uint32_t name, uint32_ spdlog::warn("Register seat again although already existing!"); return; } + version = std::min(version, wl_seat_interface.version); seat_ = static_cast(wl_registry_bind(registry, name, &wl_seat_interface, version)); } From 7a0c0ca6134dc777e179980d77fc76ca9adb4410 Mon Sep 17 00:00:00 2001 From: 1sixth <67363572+1sixth@users.noreply.github.com> Date: Wed, 28 Oct 2020 19:39:50 +0800 Subject: [PATCH 098/355] replace lowercase "k" with uppercase "K" Other units are all uppercased, so using an uppercased "K" makes it look more consistent (especially when {bandwidthUpBits} or something like that is used). --- include/util/format.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/util/format.hpp b/include/util/format.hpp index 288d8f0..d7d1609 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -42,7 +42,7 @@ namespace fmt { template auto format(const pow_format& s, FormatContext &ctx) -> decltype (ctx.out()) { - const char* units[] = { "", "k", "M", "G", "T", "P", nullptr}; + const char* units[] = { "", "K", "M", "G", "T", "P", nullptr}; auto base = s.binary_ ? 1024ull : 1000ll; auto fraction = (double) s.val_; From 2b3d7be9cb7253d852db0214f7c492d6aec8f74a Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 20 Oct 2020 23:18:58 -0700 Subject: [PATCH 099/355] feat(bar): let gtk-layer-shell manage exclusive zone Previous attempts to use auto exclusive zone from gtk-layer-shell failed because gls was expecting real booleans (`TRUE`/`FALSE`) as set_anchor arguments. With that being fixed, gtk_layer_auto_exclusive_zone_enable makes gls handle everything related to the bar resizing. The only remaining purpose of onConfigureGLS is to log warnings and bar size changes; gtk-layer-shell code path no longer needs saved width_ or height_ values. --- src/bar.cpp | 72 ++++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index 8af6b97..7f60b2f 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -2,10 +2,11 @@ #include #endif +#include + #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), @@ -134,33 +135,32 @@ void waybar::Bar::initGtkLayerShell() { 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_anchor(gtk_window, + GTK_LAYER_SHELL_EDGE_LEFT, + (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) ? TRUE : FALSE); + gtk_layer_set_anchor(gtk_window, + GTK_LAYER_SHELL_EDGE_RIGHT, + (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) ? TRUE : FALSE); + gtk_layer_set_anchor(gtk_window, + GTK_LAYER_SHELL_EDGE_TOP, + (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) ? TRUE : FALSE); + gtk_layer_set_anchor(gtk_window, + GTK_LAYER_SHELL_EDGE_BOTTOM, + (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) ? TRUE : FALSE); 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); - if (width_ > 1 && height_ > 1) { - /* configure events are not emitted if the bar is using initial size */ - setExclusiveZone(width_, height_); - } + setExclusiveZone(width_, height_); } void waybar::Bar::onConfigureGLS(GdkEventConfigure* ev) { /* * GTK wants new size for the window. - * Actual resizing is done within the gtk-layer-shell code; the only remaining action is to apply - * exclusive zone. - * gtk_layer_auto_exclusive_zone_enable() could handle even that, but at the cost of ignoring - * margins on unanchored edge. + * Actual resizing and management of the exclusve zone is handled within the gtk-layer-shell code. + * This event handler only updates stored size of the window and prints some warnings. * * Note: forced resizing to a window smaller than required by GTK would not work with * gtk-layer-shell. @@ -170,14 +170,13 @@ void waybar::Bar::onConfigureGLS(GdkEventConfigure* ev) { spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); } } else { - if (!vertical && height_ > 1 && ev->height > static_cast(height_)) { + if (height_ > 1 && ev->height > static_cast(height_)) { spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); } } width_ = ev->width; height_ = ev->height; spdlog::info(BAR_SIZE_MSG, width_, height_, output->name); - setExclusiveZone(width_, height_); } void waybar::Bar::onMapGLS(GdkEventAny* ev) { @@ -262,26 +261,31 @@ void waybar::Bar::onMap(GdkEventAny* ev) { } 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); + if (visible) { + spdlog::debug("Enable auto exclusive zone for output {}", output->name); + gtk_layer_auto_exclusive_zone_enable(window.gobj()); + } else { + spdlog::debug("Disable exclusive zone for output {}", output->name); + gtk_layer_set_exclusive_zone(window.gobj(), 0); + } } else #endif { + 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); zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_, zone); } } From 7735c80d0e421ab0787f99db6c269895ef856729 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 21 Oct 2020 22:16:12 -0700 Subject: [PATCH 100/355] refactor(bar): Split GLS and raw layer-shell implementations Extract two surface implementations from the bar class: GLSSurfaceImpl and RawSurfaceImpl. This change allowed to remove _all_ surface type conditionals and significantly simplify the Bar code. The change also applies PImpl pattern to the Bar, allowing to remove some headers and fields from `bar.hpp`. --- include/bar.hpp | 62 ++--- src/bar.cpp | 644 ++++++++++++++++++++++++++++-------------------- 2 files changed, 408 insertions(+), 298 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index fdc5a73..df50327 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -7,6 +7,7 @@ #include #include #include + #include "AModule.hpp" #include "idle-inhibit-unstable-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" @@ -23,13 +24,35 @@ struct waybar_output { nullptr, &zxdg_output_v1_destroy}; }; +struct bar_margins { + int top = 0; + int right = 0; + int bottom = 0; + int left = 0; +}; + +class BarSurface { + protected: + BarSurface() = default; + + public: + virtual void set_exclusive_zone(bool enable) = 0; + virtual void set_layer(const std::string_view &layer) = 0; + virtual void set_margins(const struct bar_margins &margins) = 0; + virtual void set_position(const std::string_view &position) = 0; + virtual void set_size(uint32_t width, uint32_t height) = 0; + + virtual ~BarSurface() = default; +}; + class Bar { public: Bar(struct waybar_output *w_output, const Json::Value &); Bar(const Bar &) = delete; ~Bar() = default; - auto toggle() -> void; + void setVisible(bool visible); + void toggle(); void handleSignal(int); struct waybar_output *output; @@ -40,48 +63,15 @@ class Bar { Gtk::Window window; 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 - /* gtk-layer-shell code */ - void initGtkLayerShell(); - void onConfigureGLS(GdkEventConfigure *ev); - void onMapGLS(GdkEventAny *ev); -#endif - /* fallback layer-surface code */ - void onConfigure(GdkEventConfigure *ev); - void onRealize(); - void onMap(GdkEventAny *ev); - void setSurfaceSize(uint32_t width, uint32_t height); - /* common code */ - void setExclusiveZone(uint32_t width, uint32_t height); + void onMap(GdkEventAny *); 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; + std::unique_ptr surface_impl_; uint32_t width_ = 0; uint32_t height_ = 1; - uint8_t anchor_; Gtk::Box left_; Gtk::Box center_; Gtk::Box right_; diff --git a/src/bar.cpp b/src/bar.cpp index 7f60b2f..a684f12 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -8,13 +8,346 @@ #include "client.hpp" #include "factory.hpp" +namespace waybar { +static constexpr const char* MIN_HEIGHT_MSG = + "Requested height: {} exceeds the minimum height: {} required by the modules"; + +static constexpr const char* MIN_WIDTH_MSG = + "Requested width: {} exceeds the minimum width: {} required by the modules"; + +static constexpr const char* BAR_SIZE_MSG = "Bar configured (width: {}, height: {}) for output: {}"; + +static constexpr const char* SIZE_DEFINED = + "{} size is defined in the config file so it will stay like that"; + +#ifdef HAVE_GTK_LAYER_SHELL +struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { + GLSSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} { + output_name_ = output.name; + // this has to be executed before GtkWindow.realize + gtk_layer_init_for_window(window_.gobj()); + gtk_layer_set_keyboard_interactivity(window.gobj(), FALSE); + gtk_layer_set_monitor(window_.gobj(), output.monitor->gobj()); + gtk_layer_set_namespace(window_.gobj(), "waybar"); + + window.signal_configure_event().connect_notify( + sigc::mem_fun(*this, &GLSSurfaceImpl::on_configure)); + } + + void set_exclusive_zone(bool enable) override { + if (enable) { + gtk_layer_auto_exclusive_zone_enable(window_.gobj()); + } else { + gtk_layer_set_exclusive_zone(window_.gobj(), 0); + } + } + + void set_margins(const struct bar_margins& margins) override { + gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_LEFT, margins.left); + gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, margins.right); + gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_TOP, margins.top); + gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, margins.bottom); + } + + void set_layer(const std::string_view& value) override { + auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM; + if (value == "top") { + layer = GTK_LAYER_SHELL_LAYER_TOP; + } else if (value == "overlay") { + layer = GTK_LAYER_SHELL_LAYER_OVERLAY; + } + gtk_layer_set_layer(window_.gobj(), layer); + } + + void set_position(const std::string_view& position) override { + auto unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM; + vertical_ = false; + if (position == "bottom") { + unanchored = GTK_LAYER_SHELL_EDGE_TOP; + } else if (position == "left") { + unanchored = GTK_LAYER_SHELL_EDGE_RIGHT; + vertical_ = true; + } else if (position == "right") { + vertical_ = true; + unanchored = GTK_LAYER_SHELL_EDGE_LEFT; + } + for (auto edge : {GTK_LAYER_SHELL_EDGE_LEFT, + GTK_LAYER_SHELL_EDGE_RIGHT, + GTK_LAYER_SHELL_EDGE_TOP, + GTK_LAYER_SHELL_EDGE_BOTTOM}) { + gtk_layer_set_anchor(window_.gobj(), edge, unanchored != edge); + } + } + + void set_size(uint32_t width, uint32_t height) override { + width_ = width; + height_ = height; + window_.set_size_request(width_, height_); + }; + + private: + Gtk::Window& window_; + std::string output_name_; + uint32_t width_; + uint32_t height_; + bool vertical_ = false; + + void on_configure(GdkEventConfigure* ev) { + /* + * GTK wants new size for the window. + * Actual resizing and management of the exclusve zone is handled within the gtk-layer-shell + * code. This event handler only updates stored size of the window and prints some warnings. + * + * Note: forced resizing to a window smaller than required by GTK would not work with + * gtk-layer-shell. + */ + if (vertical_) { + if (width_ > 1 && ev->width > static_cast(width_)) { + spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); + } + } else { + if (height_ > 1 && ev->height > static_cast(height_)) { + spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); + } + } + width_ = ev->width; + height_ = ev->height; + spdlog::info(BAR_SIZE_MSG, width_, height_, output_name_); + } +}; +#endif + +struct RawSurfaceImpl : public BarSurface, public sigc::trackable { + RawSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} { + output_ = gdk_wayland_monitor_get_wl_output(output.monitor->gobj()); + output_name_ = output.name; + + window.signal_realize().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::on_realize)); + window.signal_map_event().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::on_map)); + window.signal_configure_event().connect_notify( + sigc::mem_fun(*this, &RawSurfaceImpl::on_configure)); + + if (window.get_realized()) { + on_realize(); + } + } + + void set_exclusive_zone(bool enable) override { + exclusive_zone_ = enable; + if (layer_surface_) { + auto zone = 0; + if (enable) { + // exclusive zone already includes margin for anchored edge, + // only opposite margin should be added + if ((anchor_ & VERTICAL_ANCHOR) == VERTICAL_ANCHOR) { + zone += width_; + zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) ? margins_.right : margins_.left; + } else { + zone += height_; + zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) ? margins_.bottom : margins_.top; + } + } + spdlog::debug("Set exclusive zone {} for output {}", zone, output_name_); + zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_, zone); + } + } + + void set_layer(const std::string_view& layer) override { + layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; + if (layer == "top") { + layer_ = ZWLR_LAYER_SHELL_V1_LAYER_TOP; + } else if (layer == "overlay") { + layer_ = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; + } + // updating already mapped window + if (layer_surface_) { + if (zwlr_layer_surface_v1_get_version(layer_surface_) >= + ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION) { + zwlr_layer_surface_v1_set_layer(layer_surface_, layer_); + commit(); + } else { + spdlog::warn("Unable to set layer: layer-shell interface version is too old"); + } + } + } + + void set_margins(const struct bar_margins& margins) override { + margins_ = margins; + // updating already mapped window + if (layer_surface_) { + zwlr_layer_surface_v1_set_margin( + layer_surface_, margins_.top, margins_.right, margins_.bottom, margins_.left); + commit(); + } + } + + void set_position(const std::string_view& position) override { + anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; + if (position == "bottom") { + anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + } else if (position == "left") { + anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; + } else if (position == "right") { + anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + } + + // updating already mapped window + if (layer_surface_) { + zwlr_layer_surface_v1_set_anchor(layer_surface_, anchor_); + commit(); + } + } + + void set_size(uint32_t width, uint32_t height) override { + width_ = width; + height_ = height; + // layer_shell.configure handler should update exclusive zone if size changes + window_.set_size_request(width, height); + }; + + private: + constexpr static uint8_t VERTICAL_ANCHOR = + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + constexpr static uint8_t HORIZONTAL_ANCHOR = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + + Gtk::Window& window_; + std::string output_name_; + uint32_t width_; + uint32_t height_; + uint8_t anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; + bool exclusive_zone_ = true; + struct bar_margins margins_; + + zwlr_layer_shell_v1_layer layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; + struct wl_output* output_ = nullptr; + struct wl_surface* surface_ = nullptr; + struct zwlr_layer_surface_v1* layer_surface_ = nullptr; + + void on_realize() { + auto gdk_window = window_.get_window()->gobj(); + gdk_wayland_window_set_use_custom_surface(gdk_window); + } + + void on_map(GdkEventAny* ev) { + auto client = Client::inst(); + auto gdk_window = window_.get_window()->gobj(); + surface_ = gdk_wayland_window_get_wl_surface(gdk_window); + + layer_surface_ = zwlr_layer_shell_v1_get_layer_surface( + client->layer_shell, surface_, 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); + set_surface_size(width_, height_); + set_exclusive_zone(exclusive_zone_); + + static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = on_surface_configure, + .closed = on_surface_closed, + }; + zwlr_layer_surface_v1_add_listener(layer_surface_, &layer_surface_listener, this); + + wl_surface_commit(surface_); + wl_display_roundtrip(client->wl_display); + } + + void on_configure(GdkEventConfigure* ev) { + /* + * GTK wants new size for the window. + * + * Prefer configured size if it's non-default. + * If the size is not set and the window is smaller than requested by GTK, request resize from + * layer surface. + */ + auto tmp_height = height_; + auto tmp_width = width_; + if (ev->height > static_cast(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 (tmp_width != width_ || tmp_height != height_) { + set_surface_size(tmp_width, tmp_height); + } + } + + void commit() { + if (window_.get_mapped()) { + wl_surface_commit(surface_); + } + } + + void set_surface_size(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. + */ + bool vertical = (anchor_ & VERTICAL_ANCHOR) == VERTICAL_ANCHOR; + 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); + } + + static void on_surface_configure(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->set_exclusive_zone(o->exclusive_zone_); + spdlog::info(BAR_SIZE_MSG, + o->width_ == 1 ? "auto" : std::to_string(o->width_), + o->height_ == 1 ? "auto" : std::to_string(o->height_), + o->output_name_); + wl_surface_commit(o->surface_); + } + zwlr_layer_surface_v1_ack_configure(surface, serial); + } + + static void on_surface_closed(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; + } + } +}; + +}; // namespace waybar + waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) : output(w_output), config(w_config), - surface(nullptr), window{Gtk::WindowType::WINDOW_TOPLEVEL}, - 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), @@ -29,32 +362,22 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) center_.get_style_context()->add_class("modules-center"); right_.get_style_context()->add_class("modules-right"); - if (config["position"] == "right" || config["position"] == "left") { + auto position = config["position"].asString(); + + if (position == "right" || position == "left") { + 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; + 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; - } + struct bar_margins margins_; if (config["margin-top"].isInt() || config["margin-right"].isInt() || config["margin-bottom"].isInt() || config["margin-left"].isInt()) { @@ -102,210 +425,51 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) } #ifdef HAVE_GTK_LAYER_SHELL - use_gls_ = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true; - if (use_gls_) { - initGtkLayerShell(); - window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMapGLS)); - window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigureGLS)); - } -#endif - - if (!use_gls_) { - 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 (!use_gls_ && window.get_realized()) { - onRealize(); - } - window.show_all(); -} - -#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) ? TRUE : FALSE); - gtk_layer_set_anchor(gtk_window, - GTK_LAYER_SHELL_EDGE_RIGHT, - (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) ? TRUE : FALSE); - gtk_layer_set_anchor(gtk_window, - GTK_LAYER_SHELL_EDGE_TOP, - (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) ? TRUE : FALSE); - gtk_layer_set_anchor(gtk_window, - GTK_LAYER_SHELL_EDGE_BOTTOM, - (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) ? TRUE : FALSE); - - 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); - - setExclusiveZone(width_, height_); -} - -void waybar::Bar::onConfigureGLS(GdkEventConfigure* ev) { - /* - * GTK wants new size for the window. - * Actual resizing and management of the exclusve zone is handled within the gtk-layer-shell code. - * This event handler only updates stored size of the window and prints some warnings. - * - * Note: forced resizing to a window smaller than required by GTK would not work with - * gtk-layer-shell. - */ - if (vertical) { - if (width_ > 1 && ev->width > static_cast(width_)) { - spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); - } - } else { - if (height_ > 1 && ev->height > static_cast(height_)) { - spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); - } - } - width_ = ev->width; - height_ = ev->height; - spdlog::info(BAR_SIZE_MSG, width_, height_, output->name); -} - -void waybar::Bar::onMapGLS(GdkEventAny* ev) { - /* - * Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor). - */ - auto gdk_window = window.get_window(); - surface = gdk_wayland_window_get_wl_surface(gdk_window->gobj()); -} - -#endif - -void waybar::Bar::onConfigure(GdkEventConfigure* ev) { - /* - * GTK wants new size for the window. - * - * Prefer configured size if it's non-default. - * If the size is not set and the window is smaller than requested by GTK, request resize from - * layer surface. - */ - auto tmp_height = height_; - auto tmp_width = width_; - if (ev->height > static_cast(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 (tmp_width != width_ || tmp_height != height_) { - setSurfaceSize(tmp_width, tmp_height); - } -} - -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); - - 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) { -#ifdef HAVE_GTK_LAYER_SHELL - if (use_gls_) { - if (visible) { - spdlog::debug("Enable auto exclusive zone for output {}", output->name); - gtk_layer_auto_exclusive_zone_enable(window.gobj()); - } else { - spdlog::debug("Disable exclusive zone for output {}", output->name); - gtk_layer_set_exclusive_zone(window.gobj(), 0); - } + bool use_gls = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true; + if (use_gls) { + surface_impl_ = std::make_unique(window, *output); } else #endif { - 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); - zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_, zone); + surface_impl_ = std::make_unique(window, *output); } + + if (config["layer"].isString()) { + surface_impl_->set_layer(config["layer"].asString()); + } + surface_impl_->set_exclusive_zone(true); + surface_impl_->set_margins(margins_); + surface_impl_->set_position(position); + surface_impl_->set_size(width_, height_); + + window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap)); + + setupWidgets(); + window.show_all(); } -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. +void waybar::Bar::onMap(GdkEventAny*) { + /* + * Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor). */ - 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); + auto gdk_window = window.get_window()->gobj(); + surface = gdk_wayland_window_get_wl_surface(gdk_window); } +void waybar::Bar::setVisible(bool value) { + visible = value; + 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); + } + surface_impl_->set_exclusive_zone(visible); +} + +void waybar::Bar::toggle() { setVisible(!visible); } + // 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)) { @@ -367,50 +531,6 @@ void waybar::Bar::handleSignal(int signal) { } } -void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surface_v1* surface, - uint32_t serial, uint32_t width, uint32_t height) { - auto o = static_cast(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_); - if (!use_gls_) { - 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]) { From f01996ae997d4d2aa8ce9904332d99f12f3e4e2c Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 28 Oct 2020 08:06:40 -0700 Subject: [PATCH 101/355] fix(bar): CamelCase SurfaceImpl method names --- include/bar.hpp | 10 +++---- src/bar.cpp | 70 ++++++++++++++++++++++++------------------------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index df50327..1e3d37b 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -36,11 +36,11 @@ class BarSurface { BarSurface() = default; public: - virtual void set_exclusive_zone(bool enable) = 0; - virtual void set_layer(const std::string_view &layer) = 0; - virtual void set_margins(const struct bar_margins &margins) = 0; - virtual void set_position(const std::string_view &position) = 0; - virtual void set_size(uint32_t width, uint32_t height) = 0; + virtual void setExclusiveZone(bool enable) = 0; + virtual void setLayer(const std::string_view &layer) = 0; + virtual void setMargins(const struct bar_margins &margins) = 0; + virtual void setPosition(const std::string_view &position) = 0; + virtual void setSize(uint32_t width, uint32_t height) = 0; virtual ~BarSurface() = default; }; diff --git a/src/bar.cpp b/src/bar.cpp index a684f12..c7387e3 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -31,10 +31,10 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { gtk_layer_set_namespace(window_.gobj(), "waybar"); window.signal_configure_event().connect_notify( - sigc::mem_fun(*this, &GLSSurfaceImpl::on_configure)); + sigc::mem_fun(*this, &GLSSurfaceImpl::onConfigure)); } - void set_exclusive_zone(bool enable) override { + void setExclusiveZone(bool enable) override { if (enable) { gtk_layer_auto_exclusive_zone_enable(window_.gobj()); } else { @@ -42,14 +42,14 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { } } - void set_margins(const struct bar_margins& margins) override { + void setMargins(const struct bar_margins& margins) override { gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_LEFT, margins.left); gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, margins.right); gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_TOP, margins.top); gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, margins.bottom); } - void set_layer(const std::string_view& value) override { + void setLayer(const std::string_view& value) override { auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM; if (value == "top") { layer = GTK_LAYER_SHELL_LAYER_TOP; @@ -59,7 +59,7 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { gtk_layer_set_layer(window_.gobj(), layer); } - void set_position(const std::string_view& position) override { + void setPosition(const std::string_view& position) override { auto unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM; vertical_ = false; if (position == "bottom") { @@ -79,7 +79,7 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { } } - void set_size(uint32_t width, uint32_t height) override { + void setSize(uint32_t width, uint32_t height) override { width_ = width; height_ = height; window_.set_size_request(width_, height_); @@ -92,7 +92,7 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { uint32_t height_; bool vertical_ = false; - void on_configure(GdkEventConfigure* ev) { + void onConfigure(GdkEventConfigure* ev) { /* * GTK wants new size for the window. * Actual resizing and management of the exclusve zone is handled within the gtk-layer-shell @@ -122,17 +122,17 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { output_ = gdk_wayland_monitor_get_wl_output(output.monitor->gobj()); output_name_ = output.name; - window.signal_realize().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::on_realize)); - window.signal_map_event().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::on_map)); + window.signal_realize().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::onRealize)); + window.signal_map_event().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::onMap)); window.signal_configure_event().connect_notify( - sigc::mem_fun(*this, &RawSurfaceImpl::on_configure)); + sigc::mem_fun(*this, &RawSurfaceImpl::onConfigure)); if (window.get_realized()) { - on_realize(); + onRealize(); } } - void set_exclusive_zone(bool enable) override { + void setExclusiveZone(bool enable) override { exclusive_zone_ = enable; if (layer_surface_) { auto zone = 0; @@ -152,7 +152,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } } - void set_layer(const std::string_view& layer) override { + void setLayer(const std::string_view& layer) override { layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; if (layer == "top") { layer_ = ZWLR_LAYER_SHELL_V1_LAYER_TOP; @@ -171,7 +171,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } } - void set_margins(const struct bar_margins& margins) override { + void setMargins(const struct bar_margins& margins) override { margins_ = margins; // updating already mapped window if (layer_surface_) { @@ -181,7 +181,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } } - void set_position(const std::string_view& position) override { + void setPosition(const std::string_view& position) override { anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; if (position == "bottom") { anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; @@ -198,7 +198,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } } - void set_size(uint32_t width, uint32_t height) override { + void setSize(uint32_t width, uint32_t height) override { width_ = width; height_ = height; // layer_shell.configure handler should update exclusive zone if size changes @@ -224,12 +224,12 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { struct wl_surface* surface_ = nullptr; struct zwlr_layer_surface_v1* layer_surface_ = nullptr; - void on_realize() { + void onRealize() { auto gdk_window = window_.get_window()->gobj(); gdk_wayland_window_set_use_custom_surface(gdk_window); } - void on_map(GdkEventAny* ev) { + void onMap(GdkEventAny* ev) { auto client = Client::inst(); auto gdk_window = window_.get_window()->gobj(); surface_ = gdk_wayland_window_get_wl_surface(gdk_window); @@ -242,12 +242,12 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { 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); - set_surface_size(width_, height_); - set_exclusive_zone(exclusive_zone_); + setSurfaceSize(width_, height_); + setExclusiveZone(exclusive_zone_); static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { - .configure = on_surface_configure, - .closed = on_surface_closed, + .configure = onSurfaceConfigure, + .closed = onSurfaceClosed, }; zwlr_layer_surface_v1_add_listener(layer_surface_, &layer_surface_listener, this); @@ -255,7 +255,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { wl_display_roundtrip(client->wl_display); } - void on_configure(GdkEventConfigure* ev) { + void onConfigure(GdkEventConfigure* ev) { /* * GTK wants new size for the window. * @@ -288,7 +288,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { tmp_width = ev->width; } if (tmp_width != width_ || tmp_height != height_) { - set_surface_size(tmp_width, tmp_height); + setSurfaceSize(tmp_width, tmp_height); } } @@ -298,7 +298,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } } - void set_surface_size(uint32_t width, uint32_t height) { + void setSurfaceSize(uint32_t width, uint32_t height) { /* If the client is anchored to two opposite edges, layer_surface.configure will return * size without margins for the axis. * layer_surface.set_size, however, expects size with margins for the anchored axis. @@ -315,15 +315,15 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { zwlr_layer_surface_v1_set_size(layer_surface_, width, height); } - static void on_surface_configure(void* data, struct zwlr_layer_surface_v1* surface, - uint32_t serial, uint32_t width, uint32_t height) { + static void onSurfaceConfigure(void* data, struct zwlr_layer_surface_v1* surface, uint32_t serial, + uint32_t width, uint32_t height) { auto o = static_cast(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->set_exclusive_zone(o->exclusive_zone_); + o->setExclusiveZone(o->exclusive_zone_); spdlog::info(BAR_SIZE_MSG, o->width_ == 1 ? "auto" : std::to_string(o->width_), o->height_ == 1 ? "auto" : std::to_string(o->height_), @@ -333,7 +333,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { zwlr_layer_surface_v1_ack_configure(surface, serial); } - static void on_surface_closed(void* data, struct zwlr_layer_surface_v1* /* surface */) { + static void onSurfaceClosed(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_); @@ -435,12 +435,12 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) } if (config["layer"].isString()) { - surface_impl_->set_layer(config["layer"].asString()); + surface_impl_->setLayer(config["layer"].asString()); } - surface_impl_->set_exclusive_zone(true); - surface_impl_->set_margins(margins_); - surface_impl_->set_position(position); - surface_impl_->set_size(width_, height_); + surface_impl_->setExclusiveZone(true); + surface_impl_->setMargins(margins_); + surface_impl_->setPosition(position); + surface_impl_->setSize(width_, height_); window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap)); @@ -465,7 +465,7 @@ void waybar::Bar::setVisible(bool value) { window.get_style_context()->remove_class("hidden"); window.set_opacity(1); } - surface_impl_->set_exclusive_zone(visible); + surface_impl_->setExclusiveZone(visible); } void waybar::Bar::toggle() { setVisible(!visible); } From f97de599dde49943d2742db2903b83769601deac Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 21 Oct 2020 22:45:03 -0700 Subject: [PATCH 102/355] refactor: header cleanup Replace a couple of header includes with forward declarations. --- include/bar.hpp | 2 -- include/client.hpp | 4 ++++ src/bar.cpp | 1 + src/client.cpp | 3 +++ src/modules/idle_inhibitor.cpp | 2 ++ 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 1e3d37b..d4748d5 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -9,8 +9,6 @@ #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 { diff --git a/include/client.hpp b/include/client.hpp index 39b6ae3..05215cc 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -8,6 +8,10 @@ #include #include "bar.hpp" +struct zwlr_layer_shell_v1; +struct zwp_idle_inhibitor_v1; +struct zwp_idle_inhibit_manager_v1; + namespace waybar { class Client { diff --git a/src/bar.cpp b/src/bar.cpp index c7387e3..fb04f62 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -7,6 +7,7 @@ #include "bar.hpp" #include "client.hpp" #include "factory.hpp" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" namespace waybar { static constexpr const char* MIN_HEIGHT_MSG = diff --git a/src/client.cpp b/src/client.cpp index 9dd93d8..005761e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -6,6 +6,9 @@ #include "util/clara.hpp" #include "util/json.hpp" +#include "idle-inhibit-unstable-v1-client-protocol.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" + waybar::Client *waybar::Client::inst() { static auto c = new Client(); return c; diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index d94e957..d2cfe90 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -1,4 +1,6 @@ #include "modules/idle_inhibitor.hpp" + +#include "idle-inhibit-unstable-v1-client-protocol.h" #include "util/command.hpp" waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar, From d4d35e6b2b6f142844e18c69164c38d6b02f185a Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Thu, 22 Oct 2020 08:04:57 -0700 Subject: [PATCH 103/355] chore(subprojects): update gtk-layer-shell to 0.4.0 --- subprojects/gtk-layer-shell.wrap | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/subprojects/gtk-layer-shell.wrap b/subprojects/gtk-layer-shell.wrap index 6fe68c3..555fbcb 100644 --- a/subprojects/gtk-layer-shell.wrap +++ b/subprojects/gtk-layer-shell.wrap @@ -1,5 +1,5 @@ [wrap-file] -directory = gtk-layer-shell-0.3.0 -source_filename = gtk-layer-shell-0.3.0.tar.gz -source_hash = edd5e31279d494df66da9e9190c219fa295da547f5538207685e98468dbc134d -source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.3.0/gtk-layer-shell-0.3.0.tar.gz +directory = gtk-layer-shell-0.4.0 +source_filename = gtk-layer-shell-0.4.0.tar.gz +source_hash = 52fd74d3161fefa5528585ca5a523c3150934961f2284ad010ae54336dad097e +source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.4.0/gtk-layer-shell-0.4.0.tar.gz From 591a417b7db973304e788ccf58bbbfdb289ed9a7 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 23 Oct 2020 00:13:23 -0700 Subject: [PATCH 104/355] fix(bar): rework surface commit calls for RawSurfaceImpl wayland log shows that some changes were committed twice and some weren't committed until the next redraw. --- include/bar.hpp | 1 + src/bar.cpp | 34 ++++++++++++++++------------------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index d4748d5..8348070 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -39,6 +39,7 @@ class BarSurface { virtual void setMargins(const struct bar_margins &margins) = 0; virtual void setPosition(const std::string_view &position) = 0; virtual void setSize(uint32_t width, uint32_t height) = 0; + virtual void commit(){}; virtual ~BarSurface() = default; }; diff --git a/src/bar.cpp b/src/bar.cpp index fb04f62..8e5d7b0 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -165,7 +165,6 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { if (zwlr_layer_surface_v1_get_version(layer_surface_) >= ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION) { zwlr_layer_surface_v1_set_layer(layer_surface_, layer_); - commit(); } else { spdlog::warn("Unable to set layer: layer-shell interface version is too old"); } @@ -178,7 +177,6 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { if (layer_surface_) { zwlr_layer_surface_v1_set_margin( layer_surface_, margins_.top, margins_.right, margins_.bottom, margins_.left); - commit(); } } @@ -195,7 +193,6 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { // updating already mapped window if (layer_surface_) { zwlr_layer_surface_v1_set_anchor(layer_surface_, anchor_); - commit(); } } @@ -206,6 +203,12 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { window_.set_size_request(width, height); }; + void commit() override { + if (surface_) { + wl_surface_commit(surface_); + } + } + private: constexpr static uint8_t VERTICAL_ANCHOR = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; @@ -231,6 +234,10 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } void onMap(GdkEventAny* ev) { + static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = onSurfaceConfigure, + .closed = onSurfaceClosed, + }; auto client = Client::inst(); auto gdk_window = window_.get_window()->gobj(); surface_ = gdk_wayland_window_get_wl_surface(gdk_window); @@ -238,21 +245,16 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { layer_surface_ = zwlr_layer_shell_v1_get_layer_surface( client->layer_shell, surface_, output_, layer_, "waybar"); + zwlr_layer_surface_v1_add_listener(layer_surface_, &layer_surface_listener, this); 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(exclusive_zone_); - static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { - .configure = onSurfaceConfigure, - .closed = onSurfaceClosed, - }; - zwlr_layer_surface_v1_add_listener(layer_surface_, &layer_surface_listener, this); - - wl_surface_commit(surface_); + commit(); wl_display_roundtrip(client->wl_display); } @@ -290,12 +292,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } if (tmp_width != width_ || tmp_height != height_) { setSurfaceSize(tmp_width, tmp_height); - } - } - - void commit() { - if (window_.get_mapped()) { - wl_surface_commit(surface_); + commit(); } } @@ -329,7 +326,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { 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_); + o->commit(); } zwlr_layer_surface_v1_ack_configure(surface, serial); } @@ -467,6 +464,7 @@ void waybar::Bar::setVisible(bool value) { window.set_opacity(1); } surface_impl_->setExclusiveZone(visible); + surface_impl_->commit(); } void waybar::Bar::toggle() { setVisible(!visible); } From fe3aeb36c51267b87c8a8b8fa2ea827047c25196 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 23 Oct 2020 00:49:09 -0700 Subject: [PATCH 105/355] refactor(bar): wrap layer_surface pointer in unique_ptr --- src/bar.cpp | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index 8e5d7b0..c2cf6d3 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -4,6 +4,8 @@ #include +#include + #include "bar.hpp" #include "client.hpp" #include "factory.hpp" @@ -149,7 +151,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } } spdlog::debug("Set exclusive zone {} for output {}", zone, output_name_); - zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_, zone); + zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_.get(), zone); } } @@ -162,9 +164,9 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } // updating already mapped window if (layer_surface_) { - if (zwlr_layer_surface_v1_get_version(layer_surface_) >= + if (zwlr_layer_surface_v1_get_version(layer_surface_.get()) >= ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION) { - zwlr_layer_surface_v1_set_layer(layer_surface_, layer_); + zwlr_layer_surface_v1_set_layer(layer_surface_.get(), layer_); } else { spdlog::warn("Unable to set layer: layer-shell interface version is too old"); } @@ -176,7 +178,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { // updating already mapped window if (layer_surface_) { zwlr_layer_surface_v1_set_margin( - layer_surface_, margins_.top, margins_.right, margins_.bottom, margins_.left); + layer_surface_.get(), margins_.top, margins_.right, margins_.bottom, margins_.left); } } @@ -192,7 +194,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { // updating already mapped window if (layer_surface_) { - zwlr_layer_surface_v1_set_anchor(layer_surface_, anchor_); + zwlr_layer_surface_v1_set_anchor(layer_surface_.get(), anchor_); } } @@ -215,6 +217,11 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { constexpr static uint8_t HORIZONTAL_ANCHOR = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + template + using deleter_fn = std::integral_constant; + using layer_surface_ptr = + std::unique_ptr>; + Gtk::Window& window_; std::string output_name_; uint32_t width_; @@ -223,10 +230,10 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { bool exclusive_zone_ = true; struct bar_margins margins_; - zwlr_layer_shell_v1_layer layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; - struct wl_output* output_ = nullptr; - struct wl_surface* surface_ = nullptr; - struct zwlr_layer_surface_v1* layer_surface_ = nullptr; + zwlr_layer_shell_v1_layer layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; + struct wl_output* output_ = nullptr; // owned by GTK + struct wl_surface* surface_ = nullptr; // owned by GTK + layer_surface_ptr layer_surface_; void onRealize() { auto gdk_window = window_.get_window()->gobj(); @@ -242,14 +249,14 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { auto gdk_window = window_.get_window()->gobj(); surface_ = gdk_wayland_window_get_wl_surface(gdk_window); - layer_surface_ = zwlr_layer_shell_v1_get_layer_surface( - client->layer_shell, surface_, output_, layer_, "waybar"); + layer_surface_.reset(zwlr_layer_shell_v1_get_layer_surface( + client->layer_shell, surface_, output_, layer_, "waybar")); - zwlr_layer_surface_v1_add_listener(layer_surface_, &layer_surface_listener, this); - zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_, false); - zwlr_layer_surface_v1_set_anchor(layer_surface_, anchor_); + zwlr_layer_surface_v1_add_listener(layer_surface_.get(), &layer_surface_listener, this); + zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_.get(), false); + zwlr_layer_surface_v1_set_anchor(layer_surface_.get(), anchor_); zwlr_layer_surface_v1_set_margin( - layer_surface_, margins_.top, margins_.right, margins_.bottom, margins_.left); + layer_surface_.get(), margins_.top, margins_.right, margins_.bottom, margins_.left); setSurfaceSize(width_, height_); setExclusiveZone(exclusive_zone_); @@ -310,7 +317,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { 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); + zwlr_layer_surface_v1_set_size(layer_surface_.get(), width, height); } static void onSurfaceConfigure(void* data, struct zwlr_layer_surface_v1* surface, uint32_t serial, @@ -333,10 +340,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { static void onSurfaceClosed(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->layer_surface_.reset(); } }; From fc5906dbd4a9d026a6f746ffe37ec69ee5e2e7ea Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Thu, 22 Oct 2020 23:04:58 -0700 Subject: [PATCH 106/355] feat(bar): change layer to `bottom` when hidden Invisible bar on a `top` layer would still intercept pointer events and stop them from reaching windows below. Always changing the layer to to `bottom` along with making bar invisible would prevent that. --- include/bar.hpp | 9 ++++++++- src/bar.cpp | 27 +++++++++++++++++---------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 8348070..88df8b7 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -22,6 +22,12 @@ struct waybar_output { nullptr, &zxdg_output_v1_destroy}; }; +enum class bar_layer : uint8_t { + BOTTOM, + TOP, + OVERLAY, +}; + struct bar_margins { int top = 0; int right = 0; @@ -35,7 +41,7 @@ class BarSurface { public: virtual void setExclusiveZone(bool enable) = 0; - virtual void setLayer(const std::string_view &layer) = 0; + virtual void setLayer(bar_layer layer) = 0; virtual void setMargins(const struct bar_margins &margins) = 0; virtual void setPosition(const std::string_view &position) = 0; virtual void setSize(uint32_t width, uint32_t height) = 0; @@ -71,6 +77,7 @@ class Bar { std::unique_ptr surface_impl_; uint32_t width_ = 0; uint32_t height_ = 1; + bar_layer layer_; Gtk::Box left_; Gtk::Box center_; Gtk::Box right_; diff --git a/src/bar.cpp b/src/bar.cpp index c2cf6d3..f0fe64a 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -52,11 +52,11 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, margins.bottom); } - void setLayer(const std::string_view& value) override { + void setLayer(bar_layer value) override { auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM; - if (value == "top") { + if (value == bar_layer::TOP) { layer = GTK_LAYER_SHELL_LAYER_TOP; - } else if (value == "overlay") { + } else if (value == bar_layer::OVERLAY) { layer = GTK_LAYER_SHELL_LAYER_OVERLAY; } gtk_layer_set_layer(window_.gobj(), layer); @@ -155,11 +155,11 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } } - void setLayer(const std::string_view& layer) override { + void setLayer(bar_layer layer) override { layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; - if (layer == "top") { + if (layer == bar_layer::TOP) { layer_ = ZWLR_LAYER_SHELL_V1_LAYER_TOP; - } else if (layer == "overlay") { + } else if (layer == bar_layer::OVERLAY) { layer_ = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; } // updating already mapped window @@ -168,7 +168,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION) { zwlr_layer_surface_v1_set_layer(layer_surface_.get(), layer_); } else { - spdlog::warn("Unable to set layer: layer-shell interface version is too old"); + spdlog::warn("Unable to change layer: layer-shell implementation is too old"); } } } @@ -350,6 +350,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) : output(w_output), config(w_config), window{Gtk::WindowType::WINDOW_TOPLEVEL}, + layer_{bar_layer::BOTTOM}, left_(Gtk::ORIENTATION_HORIZONTAL, 0), center_(Gtk::ORIENTATION_HORIZONTAL, 0), right_(Gtk::ORIENTATION_HORIZONTAL, 0), @@ -364,6 +365,12 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) center_.get_style_context()->add_class("modules-center"); right_.get_style_context()->add_class("modules-right"); + if (config["layer"] == "top") { + layer_ = bar_layer::TOP; + } else if (config["layer"] == "overlay") { + layer_ = bar_layer::OVERLAY; + } + auto position = config["position"].asString(); if (position == "right" || position == "left") { @@ -436,9 +443,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) surface_impl_ = std::make_unique(window, *output); } - if (config["layer"].isString()) { - surface_impl_->setLayer(config["layer"].asString()); - } + surface_impl_->setLayer(layer_); surface_impl_->setExclusiveZone(true); surface_impl_->setMargins(margins_); surface_impl_->setPosition(position); @@ -463,9 +468,11 @@ void waybar::Bar::setVisible(bool value) { if (!visible) { window.get_style_context()->add_class("hidden"); window.set_opacity(0); + surface_impl_->setLayer(bar_layer::BOTTOM); } else { window.get_style_context()->remove_class("hidden"); window.set_opacity(1); + surface_impl_->setLayer(layer_); } surface_impl_->setExclusiveZone(visible); surface_impl_->commit(); From 9c566564e1c892b4f681be3787cd8a589f95a0ec Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 23 Oct 2020 02:59:04 -0700 Subject: [PATCH 107/355] fix(bar): address some of RawSurfaceImpl resizing issues --- include/bar.hpp | 2 -- src/bar.cpp | 55 ++++++++++++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 88df8b7..8aab8f7 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -75,8 +75,6 @@ class Bar { void setupAltFormatKeyForModuleList(const char *module_list_name); std::unique_ptr surface_impl_; - uint32_t width_ = 0; - uint32_t height_ = 1; bar_layer layer_; Gtk::Box left_; Gtk::Box center_; diff --git a/src/bar.cpp b/src/bar.cpp index f0fe64a..771adab 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -199,8 +199,8 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } void setSize(uint32_t width, uint32_t height) override { - width_ = width; - height_ = height; + configured_width_ = width_ = width; + configured_height_ = height_ = height; // layer_shell.configure handler should update exclusive zone if size changes window_.set_size_request(width, height); }; @@ -224,8 +224,10 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { Gtk::Window& window_; std::string output_name_; - uint32_t width_; - uint32_t height_; + uint32_t configured_width_ = 0; + uint32_t configured_height_ = 0; + uint32_t width_ = 0; + uint32_t height_ = 0; uint8_t anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; bool exclusive_zone_ = true; struct bar_margins margins_; @@ -280,22 +282,22 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { if (height_ > 1) { spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); } - /* - if (config["height"].isUInt()) { + if (configured_height_ > 1) { spdlog::info(SIZE_DEFINED, "Height"); - } else */ - tmp_height = ev->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()) { + if (configured_width_ > 1) { spdlog::info(SIZE_DEFINED, "Width"); - } else */ - tmp_width = ev->width; + } else { + tmp_width = ev->width; + } } if (tmp_width != width_ || tmp_height != height_) { setSurfaceSize(tmp_width, tmp_height); @@ -308,13 +310,20 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { * size without margins for the axis. * layer_surface.set_size, however, expects size with margins for the anchored axis. * This is not specified by wlr-layer-shell and based on actual behavior of sway. + * + * If the size for unanchored axis is not set (0), change request to 1 to avoid automatic + * assignment by the compositor. */ - bool vertical = (anchor_ & VERTICAL_ANCHOR) == VERTICAL_ANCHOR; - if (vertical && height > 1) { - height += margins_.top + margins_.bottom; - } - if (!vertical && width > 1) { - width += margins_.right + margins_.left; + if ((anchor_ & VERTICAL_ANCHOR) == VERTICAL_ANCHOR) { + width = width > 0 ? width : 1; + if (height > 1) { + height += margins_.top + margins_.bottom; + } + } else { + height = height > 0 ? height : 1; + if (width > 1) { + width += margins_.right + margins_.left; + } } spdlog::debug("Set surface size {}x{} for output {}", width, height, output_name_); zwlr_layer_surface_v1_set_size(layer_surface_.get(), width, height); @@ -379,12 +388,10 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); box_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); vertical = true; - - height_ = 0; - width_ = 1; } - height_ = config["height"].isUInt() ? config["height"].asUInt() : height_; - width_ = config["width"].isUInt() ? config["width"].asUInt() : width_; + + uint32_t height = config["height"].isUInt() ? config["height"].asUInt() : 0; + uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0; struct bar_margins margins_; @@ -447,7 +454,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) surface_impl_->setExclusiveZone(true); surface_impl_->setMargins(margins_); surface_impl_->setPosition(position); - surface_impl_->setSize(width_, height_); + surface_impl_->setSize(width, height); window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap)); From 96d965fe04272c522a976cc5bd6a39dc7ff2631d Mon Sep 17 00:00:00 2001 From: Laurent Arnoud Date: Sat, 10 Oct 2020 14:09:55 +0200 Subject: [PATCH 108/355] Add simpleclock as fallback when hhdate is not available ref https://github.com/Alexays/Waybar/issues/668 --- include/factory.hpp | 4 ++++ include/modules/simpleclock.hpp | 24 ++++++++++++++++++++++++ meson.build | 14 ++++++++++++-- src/modules/simpleclock.cpp | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 include/modules/simpleclock.hpp create mode 100644 src/modules/simpleclock.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 853e6b0..1cae68c 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -1,7 +1,11 @@ #pragma once #include +#ifdef HAVE_LIBDATE #include "modules/clock.hpp" +#else +#include "modules/simpleclock.hpp" +#endif #ifdef HAVE_SWAY #include "modules/sway/mode.hpp" #include "modules/sway/window.hpp" diff --git a/include/modules/simpleclock.hpp b/include/modules/simpleclock.hpp new file mode 100644 index 0000000..aa9a0a2 --- /dev/null +++ b/include/modules/simpleclock.hpp @@ -0,0 +1,24 @@ +#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 diff --git a/meson.build b/meson.build index 7f4ffac..48faeda 100644 --- a/meson.build +++ b/meson.build @@ -113,7 +113,11 @@ 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')) -tz_dep = dependency('date', default_options : [ 'use_system_tzdb=true' ], modules : [ 'date::date', 'date::date-tz' ], fallback: [ 'date', 'tz_dep' ]) +tz_dep = dependency('date', + required: false, + default_options : [ 'use_system_tzdb=true' ], + modules : [ 'date::date', 'date::date-tz' ], + fallback: [ 'date', 'tz_dep' ]) prefix = get_option('prefix') sysconfdir = get_option('sysconfdir') @@ -137,7 +141,6 @@ src_files = files( 'src/factory.cpp', 'src/AModule.cpp', 'src/ALabel.cpp', - 'src/modules/clock.cpp', 'src/modules/custom.cpp', 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', @@ -237,6 +240,13 @@ if get_option('rfkill').enabled() endif endif +if tz_dep.found() + add_project_arguments('-DHAVE_LIBDATE', language: 'cpp') + src_files += 'src/modules/clock.cpp' +else + src_files += 'src/modules/simpleclock.cpp' +endif + subdir('protocol') executable( diff --git a/src/modules/simpleclock.cpp b/src/modules/simpleclock.cpp new file mode 100644 index 0000000..3004fc2 --- /dev/null +++ b/src/modules/simpleclock.cpp @@ -0,0 +1,33 @@ +#include "modules/simpleclock.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); + } + } + // Call parent update + ALabel::update(); +} From abe1fa5bd41db67058a6a9606efcc9b4c1f99e14 Mon Sep 17 00:00:00 2001 From: Jordan Leppert Date: Sat, 31 Oct 2020 12:21:51 +0000 Subject: [PATCH 109/355] Custom module - only call label_.set_tooltip_markup if tooltip markup has actually changed - fixes tooltips not appearing at all if a custom module is updating too frequently. --- src/modules/custom.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 92eedaa..ba55edd 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -131,9 +131,13 @@ auto waybar::modules::Custom::update() -> void { label_.set_markup(str); if (tooltipEnabled()) { if (text_ == tooltip_) { - label_.set_tooltip_markup(str); + if (label_.get_tooltip_markup() != str) { + label_.set_tooltip_markup(str); + } } else { - label_.set_tooltip_markup(tooltip_); + if (label_.get_tooltip_markup() != tooltip_) { + label_.set_tooltip_markup(tooltip_); + } } } auto classes = label_.get_style_context()->list_classes(); From 4872091442e869769c5db05d96358d3b660e03ad Mon Sep 17 00:00:00 2001 From: Jordan Leppert Date: Sat, 31 Oct 2020 16:31:27 +0000 Subject: [PATCH 110/355] Draft fix for syncing idle inhibitor across outputs. The idle_inhibitor surface has been moved to Client, all instances of idle inhibitor module now use one surface between them. Any time an idle inhibitor is clicked, currently it force updates ALL modules on all outputs, this needs work. --- include/bar.hpp | 1 + include/client.hpp | 1 + include/modules/idle_inhibitor.hpp | 1 - src/bar.cpp | 12 ++++++++++++ src/modules/idle_inhibitor.cpp | 30 +++++++++++++++++++++--------- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index fdc5a73..d107784 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -31,6 +31,7 @@ class Bar { auto toggle() -> void; void handleSignal(int); + void updateAll(); struct waybar_output *output; Json::Value config; diff --git a/include/client.hpp b/include/client.hpp index 39b6ae3..b7631cc 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -22,6 +22,7 @@ class Client { 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; + struct zwp_idle_inhibitor_v1* idle_inhibitor; std::vector> bars; private: diff --git a/include/modules/idle_inhibitor.hpp b/include/modules/idle_inhibitor.hpp index 5ce324d..f1c690d 100644 --- a/include/modules/idle_inhibitor.hpp +++ b/include/modules/idle_inhibitor.hpp @@ -18,7 +18,6 @@ class IdleInhibitor : public ALabel { const Bar& bar_; std::string status_; - struct zwp_idle_inhibitor_v1* idle_inhibitor_; int pid_; }; diff --git a/src/bar.cpp b/src/bar.cpp index 8af6b97..747f0bf 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -461,3 +461,15 @@ auto waybar::Bar::setupWidgets() -> void { right_.pack_end(*module, false, false); } } + +void waybar::Bar::updateAll() { + for (auto const& module : modules_left_) { + module->update(); + } + for (auto const& module : modules_center_) { + module->update(); + } + for (auto const& module : modules_right_) { + module->update(); + } +} diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index d94e957..c6a4c78 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -6,7 +6,6 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& : 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( @@ -15,9 +14,9 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& } waybar::modules::IdleInhibitor::~IdleInhibitor() { - if (idle_inhibitor_ != nullptr) { - zwp_idle_inhibitor_v1_destroy(idle_inhibitor_); - idle_inhibitor_ = nullptr; + if (waybar::Client::inst()->idle_inhibitor != nullptr) { + zwp_idle_inhibitor_v1_destroy(waybar::Client::inst()->idle_inhibitor); + waybar::Client::inst()->idle_inhibitor = nullptr; } if (pid_ != -1) { kill(-pid_, 9); @@ -26,6 +25,13 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() { } auto waybar::modules::IdleInhibitor::update() -> void { + // Check status + if (waybar::Client::inst()->idle_inhibitor != nullptr) { + status_ = "activated"; + } else { + status_ = "deactivated"; + } + label_.set_markup( fmt::format(format_, fmt::arg("status", status_), fmt::arg("icon", getIcon(0, status_)))); label_.get_style_context()->add_class(status_); @@ -39,17 +45,23 @@ auto waybar::modules::IdleInhibitor::update() -> void { 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; + if (waybar::Client::inst()->idle_inhibitor != nullptr) { + zwp_idle_inhibitor_v1_destroy(waybar::Client::inst()->idle_inhibitor); + waybar::Client::inst()->idle_inhibitor = nullptr; status_ = "deactivated"; } else { - idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor( - waybar::Client::inst()->idle_inhibit_manager, bar_.surface); + waybar::Client::inst()->idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor( + waybar::Client::inst()->idle_inhibit_manager, bar_.surface); status_ = "activated"; } click_param = status_; } + + // Make all modules update + for (auto const& bar : waybar::Client::inst()->bars) { + bar->updateAll(); + } + ALabel::handleToggle(e); return true; } From aa4fc3dd29544586482525e20f55c8c5becb82f0 Mon Sep 17 00:00:00 2001 From: Jordan Leppert Date: Sat, 31 Oct 2020 17:30:25 +0000 Subject: [PATCH 111/355] Idle inhibitor toggle no longer update all modules - a list of idle inhibitors is maintained on the Client. --- include/bar.hpp | 1 - include/client.hpp | 3 +++ src/bar.cpp | 12 ------------ src/modules/idle_inhibitor.cpp | 9 ++++++--- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index d107784..fdc5a73 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -31,7 +31,6 @@ class Bar { auto toggle() -> void; void handleSignal(int); - void updateAll(); struct waybar_output *output; Json::Value config; diff --git a/include/client.hpp b/include/client.hpp index b7631cc..b6762f6 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -21,8 +21,11 @@ class Client { 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; struct zwp_idle_inhibitor_v1* idle_inhibitor; + std::vector idle_inhibitor_modules; + std::vector> bars; private: diff --git a/src/bar.cpp b/src/bar.cpp index 747f0bf..8af6b97 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -461,15 +461,3 @@ auto waybar::Bar::setupWidgets() -> void { right_.pack_end(*module, false, false); } } - -void waybar::Bar::updateAll() { - for (auto const& module : modules_left_) { - module->update(); - } - for (auto const& module : modules_center_) { - module->update(); - } - for (auto const& module : modules_right_) { - module->update(); - } -} diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index c6a4c78..7a0dd50 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -10,6 +10,7 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect( sigc::mem_fun(*this, &IdleInhibitor::handleToggle)); + waybar::Client::inst()->idle_inhibitor_modules.push_back(this); dp.emit(); } @@ -57,9 +58,11 @@ bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) { click_param = status_; } - // Make all modules update - for (auto const& bar : waybar::Client::inst()->bars) { - bar->updateAll(); + // Make all other idle inhibitor modules update + for (auto const& module : waybar::Client::inst()->idle_inhibitor_modules) { + if (module != this) { + module->update(); + } } ALabel::handleToggle(e); From 4889e655ebe5a58b18c294ef3b96d8dd6a46240c Mon Sep 17 00:00:00 2001 From: Jordan Leppert Date: Sun, 1 Nov 2020 13:33:28 +0000 Subject: [PATCH 112/355] Since idle_inhibitor's have a surface, we should have one for each inhibitor module. Therefore, the status is stored on the Client, and all modules create or destroy their inhibitors depending on Client's idle_inhibitor_status. Also, when modules are destroyed they remove themselves from Client's idle_inhibitor_modules. --- include/client.hpp | 4 +-- include/modules/idle_inhibitor.hpp | 2 +- src/modules/idle_inhibitor.cpp | 49 ++++++++++++++++++------------ 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index b6762f6..592af8f 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -23,8 +23,8 @@ class Client { struct zxdg_output_manager_v1 * xdg_output_manager = nullptr; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr; - struct zwp_idle_inhibitor_v1* idle_inhibitor; - std::vector idle_inhibitor_modules; + std::list idle_inhibitor_modules; + std::string idle_inhibitor_status = "deactivated"; std::vector> bars; diff --git a/include/modules/idle_inhibitor.hpp b/include/modules/idle_inhibitor.hpp index f1c690d..abfc996 100644 --- a/include/modules/idle_inhibitor.hpp +++ b/include/modules/idle_inhibitor.hpp @@ -12,12 +12,12 @@ class IdleInhibitor : public ALabel { IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&); ~IdleInhibitor(); auto update() -> void; + struct zwp_idle_inhibitor_v1* idle_inhibitor_ = nullptr; private: bool handleToggle(GdkEventButton* const& e); const Bar& bar_; - std::string status_; int pid_; }; diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index 7a0dd50..fe7ee16 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -5,20 +5,26 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& const Json::Value& config) : ALabel(config, "idle_inhibitor", id, "{status}"), bar_(bar), - status_("deactivated"), pid_(-1) { event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect( sigc::mem_fun(*this, &IdleInhibitor::handleToggle)); + + // Add this to the Client's idle_inhibitor_modules waybar::Client::inst()->idle_inhibitor_modules.push_back(this); + dp.emit(); } waybar::modules::IdleInhibitor::~IdleInhibitor() { - if (waybar::Client::inst()->idle_inhibitor != nullptr) { - zwp_idle_inhibitor_v1_destroy(waybar::Client::inst()->idle_inhibitor); - waybar::Client::inst()->idle_inhibitor = nullptr; + if (idle_inhibitor_ != nullptr) { + zwp_idle_inhibitor_v1_destroy(idle_inhibitor_); + idle_inhibitor_ = nullptr; } + + // Remove this from the Client's idle_inhibitor_modules + waybar::Client::inst()->idle_inhibitor_modules.remove(this); + if (pid_ != -1) { kill(-pid_, 9); pid_ = -1; @@ -27,17 +33,24 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() { auto waybar::modules::IdleInhibitor::update() -> void { // Check status - if (waybar::Client::inst()->idle_inhibitor != nullptr) { - status_ = "activated"; + std::string status = waybar::Client::inst()->idle_inhibitor_status; + if (status == "activated") { + if (idle_inhibitor_ == nullptr) { + idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor( + waybar::Client::inst()->idle_inhibit_manager, bar_.surface); + } } else { - status_ = "deactivated"; + if (idle_inhibitor_ != nullptr) { + zwp_idle_inhibitor_v1_destroy(idle_inhibitor_); + idle_inhibitor_ = nullptr; + } } label_.set_markup( - fmt::format(format_, fmt::arg("status", status_), fmt::arg("icon", getIcon(0, status_)))); - label_.get_style_context()->add_class(status_); + 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_); + label_.set_tooltip_text(status); } // Call parent update ALabel::update(); @@ -45,17 +58,15 @@ auto waybar::modules::IdleInhibitor::update() -> void { bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) { if (e->button == 1) { - label_.get_style_context()->remove_class(status_); - if (waybar::Client::inst()->idle_inhibitor != nullptr) { - zwp_idle_inhibitor_v1_destroy(waybar::Client::inst()->idle_inhibitor); - waybar::Client::inst()->idle_inhibitor = nullptr; - status_ = "deactivated"; + std::string status = waybar::Client::inst()->idle_inhibitor_status; + label_.get_style_context()->remove_class(status); + if (status == "activated") { + status = "deactivated"; } else { - waybar::Client::inst()->idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor( - waybar::Client::inst()->idle_inhibit_manager, bar_.surface); - status_ = "activated"; + status = "activated"; } - click_param = status_; + waybar::Client::inst()->idle_inhibitor_status = status; + click_param = status; } // Make all other idle inhibitor modules update From bb33427f6532dfbf09d7bbc6755ca3e2f3946115 Mon Sep 17 00:00:00 2001 From: Jordan Leppert Date: Sun, 1 Nov 2020 13:38:58 +0000 Subject: [PATCH 113/355] Making idle_inhibitor_ private and initialised in constructor, as it was before. --- include/modules/idle_inhibitor.hpp | 2 +- src/modules/idle_inhibitor.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/modules/idle_inhibitor.hpp b/include/modules/idle_inhibitor.hpp index abfc996..a857011 100644 --- a/include/modules/idle_inhibitor.hpp +++ b/include/modules/idle_inhibitor.hpp @@ -12,12 +12,12 @@ class IdleInhibitor : public ALabel { IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&); ~IdleInhibitor(); auto update() -> void; - struct zwp_idle_inhibitor_v1* idle_inhibitor_ = nullptr; private: bool handleToggle(GdkEventButton* const& e); const Bar& bar_; + struct zwp_idle_inhibitor_v1* idle_inhibitor_; int pid_; }; diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index fe7ee16..990af2c 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -5,6 +5,7 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& const Json::Value& config) : ALabel(config, "idle_inhibitor", id, "{status}"), bar_(bar), + idle_inhibitor_(nullptr), pid_(-1) { event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect( From c6743988d3f7afee8a4952b24817eaa59a08fe49 Mon Sep 17 00:00:00 2001 From: Jordan Leppert Date: Sun, 1 Nov 2020 16:03:39 +0000 Subject: [PATCH 114/355] Removing 'click_param' as it is no longer used. --- include/ALabel.hpp | 1 - src/modules/idle_inhibitor.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/ALabel.hpp b/include/ALabel.hpp index 6848d67..5b9ac54 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -19,7 +19,6 @@ class ALabel : public AModule { protected: Gtk::Label label_; std::string format_; - std::string click_param; const std::chrono::seconds interval_; bool alt_ = false; std::string default_format_; diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index 990af2c..5eb94eb 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -67,7 +67,6 @@ bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) { status = "activated"; } waybar::Client::inst()->idle_inhibitor_status = status; - click_param = status; } // Make all other idle inhibitor modules update From 071cb86b45d5a348430feac0516794836cf77721 Mon Sep 17 00:00:00 2001 From: Jordan Leppert Date: Sun, 1 Nov 2020 17:09:48 +0000 Subject: [PATCH 115/355] Moving idle inhibitor shared stuff out of Client and into idle_inhibitor module as static members. --- include/client.hpp | 2 -- include/modules/idle_inhibitor.hpp | 2 ++ src/modules/idle_inhibitor.cpp | 15 +++++++++------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index 592af8f..902f686 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -23,8 +23,6 @@ class Client { struct zxdg_output_manager_v1 * xdg_output_manager = nullptr; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr; - std::list idle_inhibitor_modules; - std::string idle_inhibitor_status = "deactivated"; std::vector> bars; diff --git a/include/modules/idle_inhibitor.hpp b/include/modules/idle_inhibitor.hpp index a857011..f592631 100644 --- a/include/modules/idle_inhibitor.hpp +++ b/include/modules/idle_inhibitor.hpp @@ -12,6 +12,8 @@ class IdleInhibitor : public ALabel { IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&); ~IdleInhibitor(); auto update() -> void; + static std::list idle_inhibitor_modules; + static std::string idle_inhibitor_status; private: bool handleToggle(GdkEventButton* const& e); diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index 5eb94eb..ce5f991 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -1,6 +1,9 @@ #include "modules/idle_inhibitor.hpp" #include "util/command.hpp" +std::list waybar::modules::IdleInhibitor::idle_inhibitor_modules; +std::string waybar::modules::IdleInhibitor::idle_inhibitor_status = "deactivated"; + waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "idle_inhibitor", id, "{status}"), @@ -12,7 +15,7 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& sigc::mem_fun(*this, &IdleInhibitor::handleToggle)); // Add this to the Client's idle_inhibitor_modules - waybar::Client::inst()->idle_inhibitor_modules.push_back(this); + waybar::modules::IdleInhibitor::idle_inhibitor_modules.push_back(this); dp.emit(); } @@ -24,7 +27,7 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() { } // Remove this from the Client's idle_inhibitor_modules - waybar::Client::inst()->idle_inhibitor_modules.remove(this); + waybar::modules::IdleInhibitor::idle_inhibitor_modules.remove(this); if (pid_ != -1) { kill(-pid_, 9); @@ -34,7 +37,7 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() { auto waybar::modules::IdleInhibitor::update() -> void { // Check status - std::string status = waybar::Client::inst()->idle_inhibitor_status; + std::string status = waybar::modules::IdleInhibitor::idle_inhibitor_status; if (status == "activated") { if (idle_inhibitor_ == nullptr) { idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor( @@ -59,18 +62,18 @@ auto waybar::modules::IdleInhibitor::update() -> void { bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) { if (e->button == 1) { - std::string status = waybar::Client::inst()->idle_inhibitor_status; + std::string status = waybar::modules::IdleInhibitor::idle_inhibitor_status; label_.get_style_context()->remove_class(status); if (status == "activated") { status = "deactivated"; } else { status = "activated"; } - waybar::Client::inst()->idle_inhibitor_status = status; + waybar::modules::IdleInhibitor::idle_inhibitor_status = status; } // Make all other idle inhibitor modules update - for (auto const& module : waybar::Client::inst()->idle_inhibitor_modules) { + for (auto const& module : waybar::modules::IdleInhibitor::idle_inhibitor_modules) { if (module != this) { module->update(); } From a9dae931c73e44f6c18fa0fc8a3220fbea5f265b Mon Sep 17 00:00:00 2001 From: Jordan Leppert Date: Sun, 1 Nov 2020 17:14:05 +0000 Subject: [PATCH 116/355] Renaming idle_inhibitor_modules and idle_inhibitor_status to shorter, more convenient names. --- include/client.hpp | 2 -- include/modules/idle_inhibitor.hpp | 4 ++-- src/modules/idle_inhibitor.cpp | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index 902f686..39b6ae3 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -21,9 +21,7 @@ class Client { 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: diff --git a/include/modules/idle_inhibitor.hpp b/include/modules/idle_inhibitor.hpp index f592631..2cbc6f1 100644 --- a/include/modules/idle_inhibitor.hpp +++ b/include/modules/idle_inhibitor.hpp @@ -12,8 +12,8 @@ class IdleInhibitor : public ALabel { IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&); ~IdleInhibitor(); auto update() -> void; - static std::list idle_inhibitor_modules; - static std::string idle_inhibitor_status; + static std::list modules; + static std::string status; private: bool handleToggle(GdkEventButton* const& e); diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index ce5f991..96db879 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -1,8 +1,8 @@ #include "modules/idle_inhibitor.hpp" #include "util/command.hpp" -std::list waybar::modules::IdleInhibitor::idle_inhibitor_modules; -std::string waybar::modules::IdleInhibitor::idle_inhibitor_status = "deactivated"; +std::list waybar::modules::IdleInhibitor::modules; +std::string waybar::modules::IdleInhibitor::status = "deactivated"; waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar, const Json::Value& config) @@ -14,8 +14,8 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& event_box_.signal_button_press_event().connect( sigc::mem_fun(*this, &IdleInhibitor::handleToggle)); - // Add this to the Client's idle_inhibitor_modules - waybar::modules::IdleInhibitor::idle_inhibitor_modules.push_back(this); + // Add this to the modules list + waybar::modules::IdleInhibitor::modules.push_back(this); dp.emit(); } @@ -26,8 +26,8 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() { idle_inhibitor_ = nullptr; } - // Remove this from the Client's idle_inhibitor_modules - waybar::modules::IdleInhibitor::idle_inhibitor_modules.remove(this); + // Remove this from the modules list + waybar::modules::IdleInhibitor::modules.remove(this); if (pid_ != -1) { kill(-pid_, 9); @@ -37,7 +37,7 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() { auto waybar::modules::IdleInhibitor::update() -> void { // Check status - std::string status = waybar::modules::IdleInhibitor::idle_inhibitor_status; + std::string status = waybar::modules::IdleInhibitor::status; if (status == "activated") { if (idle_inhibitor_ == nullptr) { idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor( @@ -62,18 +62,18 @@ auto waybar::modules::IdleInhibitor::update() -> void { bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) { if (e->button == 1) { - std::string status = waybar::modules::IdleInhibitor::idle_inhibitor_status; + std::string status = waybar::modules::IdleInhibitor::status; label_.get_style_context()->remove_class(status); if (status == "activated") { status = "deactivated"; } else { status = "activated"; } - waybar::modules::IdleInhibitor::idle_inhibitor_status = status; + waybar::modules::IdleInhibitor::status = status; } // Make all other idle inhibitor modules update - for (auto const& module : waybar::modules::IdleInhibitor::idle_inhibitor_modules) { + for (auto const& module : waybar::modules::IdleInhibitor::modules) { if (module != this) { module->update(); } From b015836e7b55def65d83cba9b3367ce2c1636417 Mon Sep 17 00:00:00 2001 From: Jordan Leppert Date: Sun, 1 Nov 2020 18:17:51 +0000 Subject: [PATCH 117/355] Ensure style class is removed from all IdleInhibitor instances by moving it to update(). --- src/modules/idle_inhibitor.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index 96db879..24d94f5 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -37,13 +37,14 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() { auto waybar::modules::IdleInhibitor::update() -> void { // Check status - std::string status = waybar::modules::IdleInhibitor::status; if (status == "activated") { + label_.get_style_context()->remove_class("deactivated"); if (idle_inhibitor_ == nullptr) { idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor( waybar::Client::inst()->idle_inhibit_manager, bar_.surface); } } else { + label_.get_style_context()->remove_class("activated"); if (idle_inhibitor_ != nullptr) { zwp_idle_inhibitor_v1_destroy(idle_inhibitor_); idle_inhibitor_ = nullptr; @@ -62,14 +63,11 @@ auto waybar::modules::IdleInhibitor::update() -> void { bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) { if (e->button == 1) { - std::string status = waybar::modules::IdleInhibitor::status; - label_.get_style_context()->remove_class(status); if (status == "activated") { status = "deactivated"; } else { status = "activated"; } - waybar::modules::IdleInhibitor::status = status; } // Make all other idle inhibitor modules update From 9785a8901382278c1832320a2c67cfec75dcf45f Mon Sep 17 00:00:00 2001 From: Jordan Leppert Date: Sun, 1 Nov 2020 18:25:41 +0000 Subject: [PATCH 118/355] Making active a bool --- include/modules/idle_inhibitor.hpp | 4 ++-- src/modules/idle_inhibitor.cpp | 27 ++++++++++++--------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/include/modules/idle_inhibitor.hpp b/include/modules/idle_inhibitor.hpp index 2cbc6f1..4b6c097 100644 --- a/include/modules/idle_inhibitor.hpp +++ b/include/modules/idle_inhibitor.hpp @@ -12,8 +12,8 @@ class IdleInhibitor : public ALabel { IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&); ~IdleInhibitor(); auto update() -> void; - static std::list modules; - static std::string status; + static std::list modules; + static bool status; private: bool handleToggle(GdkEventButton* const& e); diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index 24d94f5..ba5d694 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -2,7 +2,7 @@ #include "util/command.hpp" std::list waybar::modules::IdleInhibitor::modules; -std::string waybar::modules::IdleInhibitor::status = "deactivated"; +bool waybar::modules::IdleInhibitor::status = false; waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar, const Json::Value& config) @@ -37,7 +37,7 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() { auto waybar::modules::IdleInhibitor::update() -> void { // Check status - if (status == "activated") { + if (status) { label_.get_style_context()->remove_class("deactivated"); if (idle_inhibitor_ == nullptr) { idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor( @@ -51,11 +51,12 @@ auto waybar::modules::IdleInhibitor::update() -> void { } } + std::string status_text = status ? "activated" : "deactivated"; label_.set_markup( - fmt::format(format_, fmt::arg("status", status), fmt::arg("icon", getIcon(0, status)))); - label_.get_style_context()->add_class(status); + fmt::format(format_, fmt::arg("status", status_text), fmt::arg("icon", getIcon(0, status_text)))); + label_.get_style_context()->add_class(status_text); if (tooltipEnabled()) { - label_.set_tooltip_text(status); + label_.set_tooltip_text(status_text); } // Call parent update ALabel::update(); @@ -63,17 +64,13 @@ auto waybar::modules::IdleInhibitor::update() -> void { bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) { if (e->button == 1) { - if (status == "activated") { - status = "deactivated"; - } else { - status = "activated"; - } - } + status = !status; - // Make all other idle inhibitor modules update - for (auto const& module : waybar::modules::IdleInhibitor::modules) { - if (module != this) { - module->update(); + // Make all other idle inhibitor modules update + for (auto const& module : waybar::modules::IdleInhibitor::modules) { + if (module != this) { + module->update(); + } } } From d8dafa7ecc189dd203617e24441012e3ac54d9a0 Mon Sep 17 00:00:00 2001 From: Arnaud Vallette d'Osia Date: Wed, 18 Nov 2020 20:12:07 +0100 Subject: [PATCH 119/355] add minimize-raise() action --- src/modules/wlr/taskbar.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 8d6fa9b..5c830e0 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -436,6 +436,14 @@ bool Task::handle_clicked(GdkEventButton *bt) activate(); else if (action == "minimize") minimize(!minimized()); + else if (action == "minimize-raise"){ + if (minimized()) + minimize(false); + else if (active()) + minimize(true); + else + activate(); + } else if (action == "maximize") maximize(!maximized()); else if (action == "fullscreen") From 82823850745d5ec367070e1a08f568c8b3a77eb8 Mon Sep 17 00:00:00 2001 From: Arnaud Vallette d'Osia Date: Sun, 22 Nov 2020 13:06:46 +0100 Subject: [PATCH 120/355] update actions on taskbar man page --- man/waybar-wlr-taskbar.5.scd | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 55d2afd..a2bff26 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -83,9 +83,10 @@ Addressed by *wlr/taskbar* # CLICK ACTIONS *activate*: Bring the application into foreground. -*minimize*: Minimize the application. -*maximize*: Maximize the application. -*fullscreen*: Set the application to fullscreen. +*minimize*: Toggle application's minimized state. +*minimize-raise*: Bring the application into foreground or toggle its minimized state. +*maximize*: Toggle application's maximized state. +*fullscreen*: Toggle application's fullscreen state. *close*: Close the application. # EXAMPLES From 374d5ae5a198611011b8f78fc97a20b7ece5a049 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 26 Nov 2020 15:10:33 +0100 Subject: [PATCH 121/355] fix: check get_icon return non nullpt --- src/modules/wlr/taskbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 5c830e0..a18c3e2 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -95,7 +95,7 @@ static std::string get_from_desktop_app_info(const std::string &app_id) if (!app_info) app_info = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix); - if (app_info) + if (app_info && app_info->get_icon() != nullptr) return app_info->get_icon()->to_string(); return ""; From 05b12602d4f1fde4c13309a0b3a70afb010d3231 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 26 Nov 2020 15:16:55 +0100 Subject: [PATCH 122/355] fix: don't check against nullptr --- src/modules/wlr/taskbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index a18c3e2..bdc980c 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -95,7 +95,7 @@ static std::string get_from_desktop_app_info(const std::string &app_id) if (!app_info) app_info = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix); - if (app_info && app_info->get_icon() != nullptr) + if (app_info && app_info->get_icon()) return app_info->get_icon()->to_string(); return ""; From 85e00b2aab89381feda50f683d79287ec9f13e86 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Thu, 26 Nov 2020 15:31:40 -0800 Subject: [PATCH 123/355] fix: build fails with meson < 0.53.0 meson.build:12:0: ERROR: Module "fs" does not exist Fixes #909 --- meson.build | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 7f4ffac..1e7e22c 100644 --- a/meson.build +++ b/meson.build @@ -2,6 +2,7 @@ project( 'waybar', 'cpp', 'c', version: '0.9.4', license: 'MIT', + meson_version: '>= 0.49.0', default_options : [ 'cpp_std=c++17', 'buildtype=release', @@ -9,8 +10,6 @@ project( ], ) -fs = import('fs') - compiler = meson.get_compiler('cpp') cpp_args = [] @@ -318,9 +317,9 @@ if scdoc.found() foreach file : man_files path = '@0@'.format(file) - basename = fs.name(path) + basename = path.split('/')[-1] - topic = basename.split('.')[-3].split('/')[-1] + topic = basename.split('.')[-3] section = basename.split('.')[-2] output = '@0@.@1@'.format(topic, section) From 2695985da04675a18f21d2ba4e09bb2c232b1eee Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Thu, 26 Nov 2020 15:38:41 -0800 Subject: [PATCH 124/355] fix: compilation error with gcc 11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ../src/modules/network.cpp:22:6: error: ‘optional’ in namespace ‘std’ does not name a template type 22 | std::optional read_netstat(std::string_view category, std::string_view key) { | ^~~~~~~~ ../src/modules/network.cpp:7:1: note: ‘std::optional’ is defined in header ‘’; did you forget to ‘#include ’? 6 | #include "util/format.hpp" +++ |+#include 7 | #ifdef WANT_RFKILL --- src/modules/network.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 74ae913..7d9da8b 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "util/format.hpp" #ifdef WANT_RFKILL #include "util/rfkill.hpp" From 3b576ae12dc1302e7c86d2e1d98d8e578debf694 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Wed, 21 Oct 2020 13:10:10 -0400 Subject: [PATCH 125/355] Add "tooltip-format" to temperature module --- man/waybar-temperature.5.scd | 5 +++++ src/modules/temperature.cpp | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 8649736..7810a59 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -50,6 +50,11 @@ Addressed by *temperature* typeof: array ++ Based on the current temperature (Celsius) and *critical-threshold* if available, the corresponding icon gets selected. The order is *low* to *high*. +*tooltip-format*: ++ + typeof: string ++ + default: {temperatureC}°C ++ + The format for the tooltip + *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index dc6b2d7..84560e8 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -40,6 +40,16 @@ auto waybar::modules::Temperature::update() -> void { fmt::arg("temperatureF", temperature_f), fmt::arg("temperatureK", temperature_k), fmt::arg("icon", getIcon(temperature_c, "", max_temp)))); + if (tooltipEnabled()) { + std::string tooltip_format = "{temperatureC}°C"; + if (config_["tooltip-format"].isString()) { + tooltip_format = config_["tooltip-format"].asString(); + } + label_.set_tooltip_text(fmt::format(tooltip_format, + fmt::arg("temperatureC", temperature_c), + fmt::arg("temperatureF", temperature_f), + fmt::arg("temperatureK", temperature_k))); + } // Call parent update ALabel::update(); } From a7056f7ccec0126f3b2a1c86b09a4d6dacef8cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Thu, 26 Nov 2020 01:02:06 +0000 Subject: [PATCH 126/355] Calculate battery state from just energy values The energy values are all that's needed to calculate the battery state. Using other values for the total capacity results in broken results in some cases. This matches the output of TLP and i3status, while also being more straightforward. --- src/modules/battery.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index beb0554..93fcc8b 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -80,18 +80,15 @@ void waybar::modules::Battery::getBatteries() { 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; @@ -102,7 +99,6 @@ const std::tuple waybar::modules::Battery::getInfos if (_status != "Unknown") { status = _status; } - total += capacity; total_power += power_now; total_energy += energy_now; total_energy_full += energy_full; @@ -120,7 +116,7 @@ const std::tuple waybar::modules::Battery::getInfos } else if (status == "Charging" && total_power != 0) { time_remaining = -(float)(total_energy_full - total_energy) / total_power; } - uint16_t capacity = total / batteries_.size(); + float capacity = ((float)total_energy * 100.0f / (float) total_energy_full); // Handle full-at if (config_["full-at"].isUInt()) { auto full_at = config_["full-at"].asUInt(); From e0cdcb6e30efbf56a862b0b4dfb7baa5677bf35d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Thu, 26 Nov 2020 02:16:57 +0000 Subject: [PATCH 127/355] Handle charging above 100% gracefully When calibrating a battery it's possible to go above 100%. Handle that gracefully by just presenting the battery as full and 100%. --- src/modules/battery.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 93fcc8b..02e05b7 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -115,6 +115,11 @@ const std::tuple waybar::modules::Battery::getInfos time_remaining = (float)total_energy / total_power; } else if (status == "Charging" && total_power != 0) { time_remaining = -(float)(total_energy_full - total_energy) / total_power; + if (time_remaining > 0.0f) { + // If we've turned positive it means the battery is past 100% and so + // just report that as no time remaining + time_remaining = 0.0f; + } } float capacity = ((float)total_energy * 100.0f / (float) total_energy_full); // Handle full-at @@ -127,6 +132,12 @@ const std::tuple waybar::modules::Battery::getInfos } } } + if (capacity > 100.f) { + // This can happen when the battery is calibrating and goes above 100% + // Handle it gracefully by clamping at 100% and presenting it as full + capacity = 100.f; + status = "Full"; + } return {capacity, time_remaining, status}; } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); From eb3f4216d402903b18c41b38cf4ddb8f0a72ad3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Thu, 26 Nov 2020 02:20:53 +0000 Subject: [PATCH 128/355] Show battery state as rounded number Round the battery charge state so that 99.9% shows as 100%. --- src/modules/battery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 02e05b7..eece435 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -138,7 +138,7 @@ const std::tuple waybar::modules::Battery::getInfos capacity = 100.f; status = "Full"; } - return {capacity, time_remaining, status}; + return {round(capacity), time_remaining, status}; } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); return {0, 0, "Unknown"}; From f45d5829577d59eb9980a31793f2feb2f0c15eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Thu, 26 Nov 2020 02:34:36 +0000 Subject: [PATCH 129/355] Always mark battery as full at 100% Since we're now clamping at 100% and rounding, mark as full at that point. Some batteries will stay in charging state for a long time while stuck at the same charge level. Just mark them as full to not be confusing. --- src/modules/battery.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index eece435..2d55370 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -134,11 +134,17 @@ const std::tuple waybar::modules::Battery::getInfos } if (capacity > 100.f) { // This can happen when the battery is calibrating and goes above 100% - // Handle it gracefully by clamping at 100% and presenting it as full + // Handle it gracefully by clamping at 100% capacity = 100.f; + } + uint8_t cap = round(capacity); + if (cap == 100) { + // If we've reached 100% just mark as full as some batteries can stay + // stuck reporting they're still charging but not yet done status = "Full"; } - return {round(capacity), time_remaining, status}; + + return {cap, time_remaining, status}; } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); return {0, 0, "Unknown"}; From 908fa2c6c239af7a27c53658940f1f20bc5fa5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Fri, 27 Nov 2020 10:55:27 +0000 Subject: [PATCH 130/355] Make the battery full-at go to 100% full-at was capped at the value instead of allowing the battery to show 100% when you were at the full-at value. Uncapping makes more sense as it makes the full-at value the new 100% and the scale goes down from there. Whereas before the battery would stay at the full-at value until it went down enough which is unrealistic. --- man/waybar-battery.5.scd | 2 +- src/modules/battery.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 917a03d..c9e6e78 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -20,7 +20,7 @@ The *battery* module displays the current capacity and state (eg. charging) of y *full-at*: ++ typeof: integer ++ - Define the max percentage of the battery, useful for an old battery, e.g. 96 + Define the max percentage of the battery, for when you've set the battery to stop charging at a lower level to save it. For example, if you've set the battery to stop at 80% that will become the new 100%. *interval*: ++ typeof: integer ++ diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 2d55370..031c949 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -127,9 +127,6 @@ const std::tuple waybar::modules::Battery::getInfos auto full_at = config_["full-at"].asUInt(); if (full_at < 100) { capacity = 100.f * capacity / full_at; - if (capacity > full_at) { - capacity = full_at; - } } } if (capacity > 100.f) { From 89ca155c43eea73593cacfa4f58e687851a0989c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Fri, 27 Nov 2020 13:56:04 +0000 Subject: [PATCH 131/355] Support hotplugging of batteries Refresh the list of batteries on update to handle hotplug correctly. Probably fixes #490. --- include/modules/battery.hpp | 6 ++-- src/modules/battery.cpp | 69 +++++++++++++++++++++++++++++-------- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index d4d20d1..f0e3c39 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -31,16 +31,16 @@ class Battery : public ALabel { private: static inline const fs::path data_dir_ = "/sys/class/power_supply/"; - void getBatteries(); + void refreshBatteries(); void worker(); const std::string getAdapterStatus(uint8_t capacity) const; const std::tuple getInfos() const; const std::string formatTimeRemaining(float hoursRemaining); - std::vector batteries_; + int global_watch; + std::map batteries_; fs::path adapter_; int fd_; - std::vector wds_; std::string old_status_; util::SleeperThread thread_; diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 031c949..84bc973 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -4,23 +4,31 @@ 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); - } + + // Watch the directory for any added or removed batteries + global_watch = inotify_add_watch(fd_, data_dir_.c_str(), IN_CREATE | IN_DELETE); + if (global_watch < 0) { + throw std::runtime_error("Could not watch for battery plug/unplug"); } + + refreshBatteries(); worker(); } waybar::modules::Battery::~Battery() { - for (auto wd : wds_) { - inotify_rm_watch(fd_, wd); + if (global_watch >= 0) { + inotify_rm_watch(fd_, global_watch); + } + for (auto it = batteries_.cbegin(); it != batteries_.cend(); it++) { + auto watch_id = (*it).second; + if (watch_id >= 0) { + inotify_rm_watch(fd_, watch_id); + } + batteries_.erase(it); } close(fd_); } @@ -43,7 +51,13 @@ void waybar::modules::Battery::worker() { }; } -void waybar::modules::Battery::getBatteries() { +void waybar::modules::Battery::refreshBatteries() { + // Mark existing list of batteries as not necessarily found + std::map check_map; + for (auto const& bat : batteries_) { + check_map[bat.first] = false; + } + try { for (auto& node : fs::directory_iterator(data_dir_)) { if (!fs::is_directory(node)) { @@ -54,12 +68,22 @@ void waybar::modules::Battery::getBatteries() { 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") && fs::exists(node.path() / "type")) { - std::string type; - std::ifstream(node.path() / "type") >> type; + std::string type; + std::ifstream(node.path() / "type") >> type; - if (!type.compare("Battery")){ - batteries_.push_back(node.path()); + if (!type.compare("Battery")){ + check_map[node.path()] = true; + auto search = batteries_.find(node.path()); + if (search == batteries_.end()) { + // We've found a new battery save it and start listening for events + auto event_path = (node.path() / "uevent"); + auto wd = inotify_add_watch(fd_, event_path.c_str(), IN_ACCESS); + if (wd < 0) { + throw std::runtime_error("Could not watch events for " + node.path().string()); } + batteries_[node.path()] = wd; + } + } } auto adap_defined = config_["adapter"].isString(); if (((adap_defined && dir_name == config_["adapter"].asString()) || !adap_defined) && @@ -76,6 +100,17 @@ void waybar::modules::Battery::getBatteries() { } throw std::runtime_error("No batteries."); } + + // Remove any batteries that are no longer present and unwatch them + for (auto const& check : check_map) { + if (!check.second) { + auto watch_id = batteries_[check.first]; + if (watch_id >= 0) { + inotify_rm_watch(fd_, watch_id); + } + batteries_.erase(check.first); + } + } } const std::tuple waybar::modules::Battery::getInfos() const { @@ -84,7 +119,8 @@ const std::tuple waybar::modules::Battery::getInfos uint32_t total_energy = 0; // μWh uint32_t total_energy_full = 0; std::string status = "Unknown"; - for (auto const& bat : batteries_) { + for (auto const& item : batteries_) { + auto bat = item.first; uint32_t power_now; uint32_t energy_full; uint32_t energy_now; @@ -175,6 +211,11 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai } auto waybar::modules::Battery::update() -> void { + // Make sure we have the correct set of batteries, in case of hotplug + // TODO: split the global watch into it's own event and only run the refresh + // when there's been a CREATE/DELETE event + refreshBatteries(); + auto [capacity, time_remaining, status] = getInfos(); if (status == "Unknown") { status = getAdapterStatus(capacity); From 31a4aff1f8328037ad8986e2ffd1a4a90af33114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Fri, 27 Nov 2020 14:23:37 +0000 Subject: [PATCH 132/355] Don't show battery estimate at 0 If we think we're done might as well not show 0h 0min as the estimate and just not show anything. --- src/modules/battery.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 84bc973..c506171 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -200,10 +200,14 @@ const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) c } const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemaining) { - hoursRemaining = std::fabs(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 (full_hours == 0 && minutes == 0) { + // Migh as well not show "0h 0min" + return ""; + } if (config_["format-time"].isString()) { format = config_["format-time"].asString(); } From c784e8170e61bc7cd36c64e0967372524cd0fd7e Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Mon, 30 Nov 2020 12:49:48 -0800 Subject: [PATCH 133/355] clock: initialize cached date We are currently using this value once before it's initialized, since we check it before we set it in Clock::calendar_text(). This was caught by Valgrind, producing the following error: ==8962== Conditional jump or move depends on uninitialised value(s) ==8962== at 0x138285: date::operator==(date::year_month_day const&, date::year_month_day const&) (date.h:2793) ==8962== by 0x135F11: waybar::modules::Clock::calendar_text[abi:cxx11](waybar::modules::waybar_time const&) (clock.cpp:111) ==8962== by 0x13587C: waybar::modules::Clock::update() (clock.cpp:62) ==8962== by 0x156EFA: waybar::Bar::getModules(waybar::Factory const&, std::__cxx11::basic_string, std::allocator > const&)::{lambda()#1}::operator()() const (bar.cpp:577) ==8962== by 0x157F39: sigc::adaptor_functor, std::allocator > const&)::{lambda()#1}>::operator()() const (adaptor_trait.h:256) ==8962== by 0x157D94: sigc::internal::slot_call0, std::allocator > const&)::{lambda()#1}, void>::call_it(sigc::internal::slot_rep*) (slot.h:136) ==8962== by 0x5177B21: Glib::DispatchNotifier::pipe_io_handler(Glib::IOCondition) (in /usr/lib/libglibmm-2.4.so.1.3.0) ==8962== by 0x517DB5B: Glib::IOSource::dispatch(sigc::slot_base*) (in /usr/lib/libglibmm-2.4.so.1.3.0) ==8962== by 0x5188B96: Glib::Source::dispatch_vfunc(_GSource*, int (*)(void*), void*) (in /usr/lib/libglibmm-2.4.so.1.3.0) ==8962== by 0x5CBC913: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6600.2) ==8962== by 0x5D107D0: ??? (in /usr/lib/libglib-2.0.so.0.6600.2) ==8962== by 0x5CBB120: g_main_context_iteration (in /usr/lib/libglib-2.0.so.0.6600.2) Initialize the value to prevent the error. --- include/modules/clock.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 643b736..b3e2634 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -29,7 +29,7 @@ class Clock : public ALabel { const date::time_zone* time_zone_; bool fixed_time_zone_; int time_zone_idx_; - date::year_month_day cached_calendar_ymd_; + date::year_month_day cached_calendar_ymd_ = date::January/1/0; std::string cached_calendar_text_; bool handleScroll(GdkEventScroll* e); From e8dbdee2385ee77c5b1f0e03dd3f3f912ee98a8e Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Mon, 30 Nov 2020 17:01:58 -0800 Subject: [PATCH 134/355] Make spdlog use the same version of fmt we use spdlog bundles a version of fmt. However, we also depend on and use fmt directly. If we don't tell spdlog not to use its bundled version, we end up with two versions of fmt in our include path, since both libraries are header-only, meaning any slight API mismatches will cause build failures and undesired behavior. We seem to have gotten lucky so far, but I ran into all sorts of issues when I tried to update to a newer version of spdlog. This change prevents them. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 1e7e22c..70067ee 100644 --- a/meson.build +++ b/meson.build @@ -80,7 +80,7 @@ is_openbsd = host_machine.system() == 'openbsd' thread_dep = dependency('threads') fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep']) -spdlog = dependency('spdlog', version : ['>=1.3.1'], fallback : ['spdlog', 'spdlog_dep']) +spdlog = dependency('spdlog', version : ['>=1.3.1'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') From ad40511358ef057c1a0a8ff9b0bcfcf61ede6ff0 Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Mon, 30 Nov 2020 17:10:02 -0800 Subject: [PATCH 135/355] Update spdlog subproject to 1.8.1 Among other changes, this adds spdlog::should_log(), which lets us easily determine whether a log message will be printed so that we can avoid extra computation when unnecessary. New wrap file taken from https://wrapdb.mesonbuild.com/spdlog and modified to download from GitHub as per commit 99dde1aff8db ("Download patch files from Github instead of wrapdb"). --- meson.build | 2 +- subprojects/spdlog.wrap | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/meson.build b/meson.build index 70067ee..7f9db5f 100644 --- a/meson.build +++ b/meson.build @@ -80,7 +80,7 @@ is_openbsd = host_machine.system() == 'openbsd' thread_dep = dependency('threads') fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep']) -spdlog = dependency('spdlog', version : ['>=1.3.1'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true']) +spdlog = dependency('spdlog', version : ['>=1.8.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') diff --git a/subprojects/spdlog.wrap b/subprojects/spdlog.wrap index 750036b..c30450e 100644 --- a/subprojects/spdlog.wrap +++ b/subprojects/spdlog.wrap @@ -1,10 +1,13 @@ [wrap-file] -directory = spdlog-1.3.1 +directory = spdlog-1.8.1 -source_url = https://github.com/gabime/spdlog/archive/v1.3.1.tar.gz -source_filename = v1.3.1.tar.gz -source_hash = 160845266e94db1d4922ef755637f6901266731c4cb3b30b45bf41efa0e6ab70 +source_url = https://github.com/gabime/spdlog/archive/v1.8.1.tar.gz +source_filename = v1.8.1.tar.gz +source_hash = 5197b3147cfcfaa67dd564db7b878e4a4b3d9f3443801722b3915cdeced656cb -patch_url = https://github.com/mesonbuild/spdlog/releases/download/1.3.1-1/spdlog.zip -patch_filename = spdlog-1.3.1-1-wrap.zip -patch_hash = 715a0229781019b853d409cc0bf891ee4b9d3a17bec0cf87f4ad30b28bbecc87 +patch_url = https://github.com/mesonbuild/spdlog/releases/download/1.8.1-1/spdlog.zip +patch_filename = spdlog-1.8.1-1-wrap.zip +patch_hash = 76844292a8e912aec78450618271a311841b33b17000988f215ddd6c64dd71b3 + +[provide] +spdlog = spdlog_dep From 85df7ce2da2caa8910151a28fcd42f46ea150a6b Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Mon, 30 Nov 2020 12:05:18 -0800 Subject: [PATCH 136/355] Add debug log message to print each bar's widget tree This is very useful when writing CSS that affects more than just a single widget. Pass `-l debug` to enable debug logging and show this information. Example output: [2020-11-30 12:38:51.141] [debug] GTK widget tree: window#waybar.background.bottom.eDP-1.:dir(ltr) decoration:dir(ltr) box.horizontal:dir(ltr) box.horizontal.modules-left:dir(ltr) widget:dir(ltr) box#workspaces.horizontal:dir(ltr) widget:dir(ltr) label#mode:dir(ltr) widget:dir(ltr) label#window:dir(ltr) box.horizontal.modules-center:dir(ltr) box.horizontal.modules-right:dir(ltr) widget:dir(ltr) box#tray.horizontal:dir(ltr) widget:dir(ltr) label#idle_inhibitor:dir(ltr) widget:dir(ltr) label#pulseaudio:dir(ltr) widget:dir(ltr) label#network:dir(ltr) widget:dir(ltr) label#cpu:dir(ltr) widget:dir(ltr) label#memory:dir(ltr) widget:dir(ltr) label#temperature:dir(ltr) widget:dir(ltr) label#backlight:dir(ltr) widget:dir(ltr) label#battery:dir(ltr) widget:dir(ltr) label#clock:dir(ltr) --- src/bar.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bar.cpp b/src/bar.cpp index 771adab..7bd6172 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -460,6 +460,16 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) setupWidgets(); window.show_all(); + + if (spdlog::should_log(spdlog::level::debug)) { + // Unfortunately, this function isn't in the C++ bindings, so we have to call the C version. + char* gtk_tree = gtk_style_context_to_string( + window.get_style_context()->gobj(), + (GtkStyleContextPrintFlags)(GTK_STYLE_CONTEXT_PRINT_RECURSE | + GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE)); + spdlog::debug("GTK widget tree:\n{}", gtk_tree); + g_free(gtk_tree); + } } void waybar::Bar::onMap(GdkEventAny*) { From 407bf27401c25a96cefd332fd9a5d9f1fef12fce Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Mon, 30 Nov 2020 17:32:06 -0800 Subject: [PATCH 137/355] Update fmt subproject to 7.1.3 There is no particular change in this update that we require. However, our previous version, 5.3.0, is nearly two years old, so it seems prudent to pull in all the upstream fixes that have been made since then. New wrap file taken from https://wrapdb.mesonbuild.com/fmt and modified to download from GitHub as per commit 99dde1aff8db ("Download patch files from Github instead of wrapdb"). --- subprojects/fmt.wrap | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap index eb79283..71abc80 100644 --- a/subprojects/fmt.wrap +++ b/subprojects/fmt.wrap @@ -1,10 +1,13 @@ [wrap-file] -directory = fmt-5.3.0 +directory = fmt-7.1.3 -source_url = https://github.com/fmtlib/fmt/archive/5.3.0.tar.gz -source_filename = fmt-5.3.0.tar.gz -source_hash = defa24a9af4c622a7134076602070b45721a43c51598c8456ec6f2c4dbb51c89 +source_url = https://github.com/fmtlib/fmt/archive/7.1.3.tar.gz +source_filename = fmt-7.1.3.tar.gz +source_hash = 5cae7072042b3043e12d53d50ef404bbb76949dad1de368d7f993a15c8c05ecc -patch_url = https://github.com/mesonbuild/fmt/releases/download/5.3.0-1/fmt.zip -patch_filename = fmt-5.3.0-1-wrap.zip -patch_hash = 18f21a3b8833949c35d4ac88a7059577d5fa24b98786e4b1b2d3d81bb811440f \ No newline at end of file +patch_url = https://github.com/mesonbuild/fmt/releases/download/7.1.3-1/fmt.zip +patch_filename = fmt-7.1.3-1-wrap.zip +patch_hash = 6eb951a51806fd6ffd596064825c39b844c1fe1799840ef507b61a53dba08213 + +[provide] +fmt = fmt_dep From 29f78e04267f726363d11a3fb135d60aa4ce1f41 Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Mon, 30 Nov 2020 18:07:22 -0800 Subject: [PATCH 138/355] Fix a few compiler warnings There was one uninitialized value warning and two mismatched-sign compare warnings. They both appear valid, the first occurring when MPD's "format-stopped" contains {songPosition} or {queueLength} and the second occurring when the clock's "timezones" array is more than 2 billion items long (not likely, I admit). Fix both issues. --- src/modules/clock.cpp | 10 ++++------ src/modules/mpd/mpd.cpp | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index f313606..5b2c3f4 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -85,13 +85,11 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) { return true; } auto nr_zones = config_["timezones"].size(); - int new_idx = time_zone_idx_ + ((dir == SCROLL_DIR::UP) ? 1 : -1); - if (new_idx < 0) { - time_zone_idx_ = nr_zones - 1; - } else if (new_idx >= nr_zones) { - time_zone_idx_ = 0; + if (dir == SCROLL_DIR::UP) { + size_t new_idx = time_zone_idx_ + 1; + time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx; } else { - time_zone_idx_ = new_idx; + time_zone_idx_ = time_zone_idx_ == 0 ? nr_zones - 1 : time_zone_idx_ - 1; } auto zone_name = config_["timezones"][time_zone_idx_]; if (!zone_name.isString() || zone_name.empty()) { diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 710f23f..98332dc 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -100,7 +100,7 @@ void waybar::modules::MPD::setLabel() { auto format = format_; std::string artist, album_artist, album, title, date; - int song_pos, queue_length; + int song_pos = 0, queue_length = 0; std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; From dd596a5c6c15cd7fa7e7033b1dccf3c9868f9d1c Mon Sep 17 00:00:00 2001 From: Jeremy Attali Date: Mon, 30 Nov 2020 23:11:51 -0500 Subject: [PATCH 139/355] fix(systemd): restart when service fails The current service doesn't play too nice with Sway when it is started from [sway service](https://github.com/xdbob/sway-services). Waybar is started before the system has a display. ``` Nov 30 22:11:23 ansan waybar[1352]: Unable to init server: Could not connect: Connection refused Nov 30 22:11:23 ansan waybar[1352]: cannot open display: Nov 30 22:11:23 ansan systemd[1306]: waybar.service: Main process exited, code=exited, status=1/FAILURE Nov 30 22:11:23 ansan systemd[1306]: waybar.service: Failed with result 'exit-code'. ``` Restarting the service after the system has been initialized works nicely, so this restart rule should do the trick without tinkering with the target. --- resources/waybar.service.in | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/waybar.service.in b/resources/waybar.service.in index 2c907e9..af5832d 100644 --- a/resources/waybar.service.in +++ b/resources/waybar.service.in @@ -6,6 +6,7 @@ After=graphical-session.target [Service] ExecStart=@prefix@/bin/waybar +Restart=on-failure [Install] WantedBy=graphical-session.target From 1fe0bcacc0f30f10c9eabf3f01782b3557c4446a Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Sat, 29 Aug 2020 23:18:26 -0700 Subject: [PATCH 140/355] style: add 4px margins to window and workspaces modules These modules, unlike others, have no horizontal margins by default. This means that they'll appear uncomfortably close together in any config that puts them side-by-side. In general, the default style should make configs with any module ordering look good. Add the same 4px horizontal margins that other module have to these. To preserve the current default appearance, exempt the workspace module from a margin on the appropriate side when it's the leftmost or rightmost module on the bar. --- resources/style.css | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/resources/style.css b/resources/style.css index 99fbbae..920bb52 100644 --- a/resources/style.css +++ b/resources/style.css @@ -83,6 +83,21 @@ window#waybar.chromium { color: #ffffff; } +#window, +#workspaces { + margin: 0 4px; +} + +/* If workspaces is the leftmost module, omit left margin */ +.modules-left > widget:first-child > #workspaces { + margin-left: 0; +} + +/* If workspaces is the rightmost module, omit right margin */ +.modules-right > widget:last-child > #workspaces { + margin-right: 0; +} + #clock { background-color: #64727D; } From 09c89bcd2024b29903ae2acc9fe13ae019f49e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Thu, 3 Dec 2020 09:52:33 +0000 Subject: [PATCH 141/355] Don't update battery list on every update Speedup battery state update by only updating the battery list when we get a CREATE/DELETE event in the directory or whenever we do a full refresh on the interval. --- include/modules/battery.hpp | 7 +++-- src/modules/battery.cpp | 53 +++++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index f0e3c39..08dd79d 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -34,16 +34,19 @@ class Battery : public ALabel { void refreshBatteries(); void worker(); const std::string getAdapterStatus(uint8_t capacity) const; - const std::tuple getInfos() const; + const std::tuple getInfos(); const std::string formatTimeRemaining(float hoursRemaining); int global_watch; std::map batteries_; fs::path adapter_; - int fd_; + int battery_watch_fd_; + int global_watch_fd_; + std::mutex battery_list_mutex_; std::string old_status_; util::SleeperThread thread_; + util::SleeperThread thread_battery_update_; util::SleeperThread thread_timer_; }; diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index c506171..0b54e9c 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -4,13 +4,18 @@ waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config) : ALabel(config, "battery", id, "{capacity}%", 60) { - fd_ = inotify_init1(IN_CLOEXEC); - if (fd_ == -1) { + battery_watch_fd_ = inotify_init1(IN_CLOEXEC); + if (battery_watch_fd_ == -1) { + throw std::runtime_error("Unable to listen batteries."); + } + + global_watch_fd_ = inotify_init1(IN_CLOEXEC); + if (global_watch_fd_ == -1) { throw std::runtime_error("Unable to listen batteries."); } // Watch the directory for any added or removed batteries - global_watch = inotify_add_watch(fd_, data_dir_.c_str(), IN_CREATE | IN_DELETE); + global_watch = inotify_add_watch(global_watch_fd_, data_dir_.c_str(), IN_CREATE | IN_DELETE); if (global_watch < 0) { throw std::runtime_error("Could not watch for battery plug/unplug"); } @@ -20,38 +25,55 @@ waybar::modules::Battery::Battery(const std::string& id, const Json::Value& conf } waybar::modules::Battery::~Battery() { + std::lock_guard guard(battery_list_mutex_); + if (global_watch >= 0) { - inotify_rm_watch(fd_, global_watch); + inotify_rm_watch(global_watch_fd_, global_watch); } + close(global_watch_fd_); + for (auto it = batteries_.cbegin(); it != batteries_.cend(); it++) { auto watch_id = (*it).second; if (watch_id >= 0) { - inotify_rm_watch(fd_, watch_id); + inotify_rm_watch(battery_watch_fd_, watch_id); } batteries_.erase(it); } - close(fd_); + close(battery_watch_fd_); } void waybar::modules::Battery::worker() { thread_timer_ = [this] { + // Make sure we eventually update the list of batteries even if we miss an + // inotify event for some reason + refreshBatteries(); dp.emit(); thread_timer_.sleep_for(interval_); }; thread_ = [this] { struct inotify_event event = {0}; - int nbytes = read(fd_, &event, sizeof(event)); + int nbytes = read(battery_watch_fd_, &event, sizeof(event)); if (nbytes != sizeof(event) || event.mask & IN_IGNORED) { thread_.stop(); return; } - // TODO: don't stop timer for now since there is some bugs :? - // thread_timer_.stop(); + dp.emit(); + }; + thread_battery_update_ = [this] { + struct inotify_event event = {0}; + int nbytes = read(global_watch_fd_, &event, sizeof(event)); + if (nbytes != sizeof(event) || event.mask & IN_IGNORED) { + thread_.stop(); + return; + } + refreshBatteries(); dp.emit(); }; } void waybar::modules::Battery::refreshBatteries() { + std::lock_guard guard(battery_list_mutex_); + // Mark existing list of batteries as not necessarily found std::map check_map; for (auto const& bat : batteries_) { @@ -77,7 +99,7 @@ void waybar::modules::Battery::refreshBatteries() { if (search == batteries_.end()) { // We've found a new battery save it and start listening for events auto event_path = (node.path() / "uevent"); - auto wd = inotify_add_watch(fd_, event_path.c_str(), IN_ACCESS); + auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS); if (wd < 0) { throw std::runtime_error("Could not watch events for " + node.path().string()); } @@ -106,14 +128,16 @@ void waybar::modules::Battery::refreshBatteries() { if (!check.second) { auto watch_id = batteries_[check.first]; if (watch_id >= 0) { - inotify_rm_watch(fd_, watch_id); + inotify_rm_watch(battery_watch_fd_, watch_id); } batteries_.erase(check.first); } } } -const std::tuple waybar::modules::Battery::getInfos() const { +const std::tuple waybar::modules::Battery::getInfos() { + std::lock_guard guard(battery_list_mutex_); + try { uint32_t total_power = 0; // μW uint32_t total_energy = 0; // μWh @@ -215,11 +239,6 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai } auto waybar::modules::Battery::update() -> void { - // Make sure we have the correct set of batteries, in case of hotplug - // TODO: split the global watch into it's own event and only run the refresh - // when there's been a CREATE/DELETE event - refreshBatteries(); - auto [capacity, time_remaining, status] = getInfos(); if (status == "Unknown") { status = getAdapterStatus(capacity); From bb60e68b9d39cb21b195cbd156d05b0987fc653f Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Thu, 3 Dec 2020 21:52:20 +0100 Subject: [PATCH 142/355] Update to the latest version of the foreign toplevel manager protocol There was an update the of the toplevel manager protocol. Unfortunately, there are no new interesting updates with regard to the taskbar implementation. Nonetheless, update the protocol xml files to the latest version so that the implementation is up-to-date. While being there, also change the debug warning that is shown when there is a version mismatch between the server and client version of the protocol. --- ...lr-foreign-toplevel-management-unstable-v1.xml | 15 +++++++++++++-- src/modules/wlr/taskbar.cpp | 13 +++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml index a97738f..1081337 100644 --- a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml +++ b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml @@ -25,7 +25,7 @@ THIS SOFTWARE. - + The purpose of this protocol is to enable the creation of taskbars and docks by providing them with a list of opened applications and @@ -68,7 +68,7 @@ - + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel window. Each app may have multiple opened toplevels. @@ -255,5 +255,16 @@ actually changes, this will be indicated by the state event. + + + + + + This event is emitted whenever the parent of the toplevel changes. + + No event is emitted when the parent handle is destroyed by the client. + + + diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index bdc980c..0a42ca1 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -187,6 +187,12 @@ static void tl_handle_done(void *data, struct zwlr_foreign_toplevel_handle_v1 *h return static_cast(data)->handle_done(); } +static void tl_handle_parent(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + struct zwlr_foreign_toplevel_handle_v1 *parent) +{ + /* This is explicitly left blank */ +} + static void tl_handle_closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) { return static_cast(data)->handle_closed(); @@ -200,6 +206,7 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_imp .state = tl_handle_state, .done = tl_handle_done, .closed = tl_handle_closed, + .parent = tl_handle_parent, }; Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, @@ -661,9 +668,11 @@ void Taskbar::register_manager(struct wl_registry *registry, uint32_t name, uint spdlog::warn("Register foreign toplevel manager again although already existing!"); return; } - if (version < ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION) { - spdlog::warn("Using different foreign toplevel manager protocol version: {}", version); + if (version < ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN_SINCE_VERSION) { + spdlog::warn("Foreign toplevel manager server does not have the appropriate version." + " To be able to use all features, you need at least version 2, but server is version {}", version); } + // limit version to a highest supported by the client protocol file version = std::min(version, zwlr_foreign_toplevel_manager_v1_interface.version); From 18f129a7128ee7a3991fba316fe956c455177360 Mon Sep 17 00:00:00 2001 From: Till Smejkal Date: Fri, 4 Dec 2020 08:04:02 +0100 Subject: [PATCH 143/355] Spit out a warning when trying to set/unset fullscreen without server supporting it Previously we only checked when connecting to the server whether it had the minimum required version but didn't act accordingly in the various functions that use the functionality of later versions. If there were a server in the wild, that actually would not have this functionality, there would have been a crash. Fix this by checking the version before using the functionality and gracefully abort it. --- src/modules/wlr/taskbar.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 0a42ca1..e70ad9c 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -546,6 +546,11 @@ void Task::activate() void Task::fullscreen(bool set) { + if (zwlr_foreign_toplevel_handle_v1_get_version(handle_) < ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN_SINCE_VERSION) { + spdlog::warn("Foreign toplevel manager server does not support for set/unset fullscreen."); + return; + } + if (set) zwlr_foreign_toplevel_handle_v1_set_fullscreen(handle_, nullptr); else From 68b6136989bf102b30a05b7b1964c44bca59a57b Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 4 Dec 2020 00:38:18 -0800 Subject: [PATCH 144/355] fix(sway/workspaces): ignore emulated scroll events GDK Wayland backend can emit two events for mouse scroll: one is a GDK_SCROLL_SMOOTH and the other one is an emulated scroll event with direction. We only receive emulated events on a window, thus it is not possible to handle these in a module and stop propagation. Ignoring emulated events should be safe since those are duplicates of smooth scroll events anyways. Fixes #386 --- src/modules/sway/workspaces.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 8d78bf5..d0c2463 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -291,6 +291,12 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node } bool Workspaces::handleScroll(GdkEventScroll *e) { + if (gdk_event_get_pointer_emulated((GdkEvent *)e)) { + /** + * Ignore emulated scroll events on window + */ + return false; + } auto dir = AModule::getScrollDir(e); if (dir == SCROLL_DIR::NONE) { return true; From 0d03c1d4da50ef248b197c29301e7cd879f98336 Mon Sep 17 00:00:00 2001 From: Andrea Scarpino Date: Fri, 4 Dec 2020 23:44:43 +0100 Subject: [PATCH 145/355] Fix waybar-pulseaudio with pipewire-pulse --- src/modules/pulseaudio.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 571a78e..b5193f5 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -178,8 +178,8 @@ void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_ser pa->default_sink_name_ = i->default_sink_name; pa->default_source_name_ = i->default_source_name; - pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data); - pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data); + pa_context_get_sink_info_by_name(context, "@DEFAULT_SINK@", sinkInfoCb, data); + pa_context_get_source_info_by_name(context, "@DEFAULT_SOURCE@", sourceInfoCb, data); } static const std::array ports = { From 50ecc972843fd6dcc514775349a8b733e1dd2468 Mon Sep 17 00:00:00 2001 From: danielrainer <34983953+danielrainer@users.noreply.github.com> Date: Sat, 12 Dec 2020 23:21:17 +0100 Subject: [PATCH 146/355] Fix typo --- man/waybar-states.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-states.5.scd b/man/waybar-states.5.scd index fe6a730..ae2df1b 100644 --- a/man/waybar-states.5.scd +++ b/man/waybar-states.5.scd @@ -13,7 +13,7 @@ apply a class when the value matches the declared state value. Each class gets activated when the current capacity is equal or below the configured **. - Also each state can have its own *format*. - Those con be configured via *format-*. + Those can be configured via *format-*. Or if you want to differentiate a bit more even as *format--*. # EXAMPLE From 85ca5027f41b4c15a7c90a5ffeef3c1f5a2d2340 Mon Sep 17 00:00:00 2001 From: Harit Kapadia Date: Fri, 18 Dec 2020 18:14:14 -0500 Subject: [PATCH 147/355] Fix Sway #waybar.solo CSS rule applying on split This error occurs because of an incorrect assumption that the size of the list of nodes that contains the focused window is the number of windows in a workspace. The windows in a workspace are stored as a tree by Sway, rather than a list, so the number of windows has to be found by counting the leaves of a workspace tree. --- src/modules/sway/window.cpp | 47 +++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index f10bf1c..64f7252 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -64,29 +64,51 @@ auto Window::update() -> void { ALabel::update(); } -std::tuple Window::getFocusedNode( - const Json::Value& nodes, std::string& output) { - for (auto const& node : nodes) { +int leafNodesInWorkspace(const Json::Value& node) { + auto const& nodes = node["nodes"]; + if(nodes.empty()) { + if(node["type"] == "workspace") + return 0; + else + return 1; + } + int sum = 0; + for(auto const& node : nodes) + sum += leafNodesInWorkspace(node); + return sum; +} + +std::tuple gfnWithWorkspace( + const Json::Value& nodes, std::string& output, const Json::Value& config_, + const Bar& bar_, Json::Value& parentWorkspace) { + for(auto const& node : nodes) { if (node["output"].isString()) { output = node["output"].asString(); } + // found node 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}; + : node["window_properties"]["instance"].asString(); + int nb = node.size(); + if(parentWorkspace != 0) + nb = leafNodesInWorkspace(parentWorkspace); + return {nb, + node["id"].asInt(), + Glib::Markup::escape_text(node["name"].asString()), + app_id}; } } - auto [nb, id, name, app_id] = getFocusedNode(node["nodes"], output); + // iterate + if(node["type"] == "workspace") + parentWorkspace = node; + auto [nb, id, name, app_id] = gfnWithWorkspace(node["nodes"], output, config_, bar_, parentWorkspace); if (id > -1 && !name.empty()) { return {nb, id, name, app_id}; } // Search for floating node - std::tie(nb, id, name, app_id) = getFocusedNode(node["floating_nodes"], output); + std::tie(nb, id, name, app_id) = gfnWithWorkspace(node["floating_nodes"], output, config_, bar_, parentWorkspace); if (id > -1 && !name.empty()) { return {nb, id, name, app_id}; } @@ -94,6 +116,11 @@ std::tuple Window::getFocusedNode( return {0, -1, "", ""}; } +std::tuple Window::getFocusedNode( + const Json::Value& nodes, std::string& output) { + return gfnWithWorkspace(nodes, output, config_, bar_, placeholder); +} + void Window::getTree() { try { ipc_.sendCmd(IPC_GET_TREE); From cb7baee045d41d32bd4d90f7e0237f1ec925b528 Mon Sep 17 00:00:00 2001 From: Harit Kapadia Date: Fri, 18 Dec 2020 18:17:17 -0500 Subject: [PATCH 148/355] Fixed compile error --- src/modules/sway/window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 64f7252..e920345 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -118,6 +118,7 @@ std::tuple gfnWithWorkspace( std::tuple Window::getFocusedNode( const Json::Value& nodes, std::string& output) { + Json::Value placeholder = 0; return gfnWithWorkspace(nodes, output, config_, bar_, placeholder); } From 4b29aef048b640f162cd4d0b7ad082fc20b1c0b8 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 23 Dec 2020 21:33:40 +0100 Subject: [PATCH 149/355] chore: v0.9.5 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 7f9db5f..839c1dc 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.4', + version: '0.9.5', license: 'MIT', meson_version: '>= 0.49.0', default_options : [ From e4340a7536d31332d8e455714d160d343d462932 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Sun, 29 Nov 2020 20:43:04 +0000 Subject: [PATCH 150/355] CI: add FreeBSD to Actions --- .github/workflows/freebsd.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/freebsd.yml diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml new file mode 100644 index 0000000..7072c49 --- /dev/null +++ b/.github/workflows/freebsd.yml @@ -0,0 +1,22 @@ +name: freebsd + +on: [ push, pull_request ] + +jobs: + clang: + runs-on: macos-latest # until https://github.com/actions/runner/issues/385 + steps: + - uses: actions/checkout@v2 + - name: Test in FreeBSD VM + uses: vmactions/freebsd-vm@v0.0.9 # aka FreeBSD 12.2 + with: + usesh: true + prepare: | + export CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib # sndio + sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf + pkg install -y git # subprojects/date + pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu sndio \ + libfmt libmpdclient libudev-devd meson pkgconf pulseaudio scdoc spdlog + run: | + meson build -Dman-pages=enabled + ninja -C build From f3911867496d4d3f119e5a5f464aa2ff30f90750 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 25 Dec 2020 09:28:05 +0100 Subject: [PATCH 151/355] Revert "Replace lowercase "k" with uppercase "K" to make it look more consistent" --- include/util/format.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/util/format.hpp b/include/util/format.hpp index d7d1609..288d8f0 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -42,7 +42,7 @@ namespace fmt { template auto format(const pow_format& s, FormatContext &ctx) -> decltype (ctx.out()) { - const char* units[] = { "", "K", "M", "G", "T", "P", nullptr}; + const char* units[] = { "", "k", "M", "G", "T", "P", nullptr}; auto base = s.binary_ ? 1024ull : 1000ll; auto fraction = (double) s.val_; From 005af7f7b743731229d6e7d7866a5628650bce68 Mon Sep 17 00:00:00 2001 From: Andrea Scarpino Date: Fri, 25 Dec 2020 17:37:21 +0100 Subject: [PATCH 152/355] Revert "Fix waybar-pulseaudio with pipewire-pulse" This reverts commit 0d03c1d4da50ef248b197c29301e7cd879f98336. --- src/modules/pulseaudio.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index b5193f5..571a78e 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -178,8 +178,8 @@ void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_ser pa->default_sink_name_ = i->default_sink_name; pa->default_source_name_ = i->default_source_name; - pa_context_get_sink_info_by_name(context, "@DEFAULT_SINK@", sinkInfoCb, data); - pa_context_get_source_info_by_name(context, "@DEFAULT_SOURCE@", sourceInfoCb, 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 = { From 0233e0eeec03f9eb00b98bbf04301852431c0cdd Mon Sep 17 00:00:00 2001 From: Andreas Backx Date: Fri, 25 Dec 2020 20:54:38 +0000 Subject: [PATCH 153/355] Added waybar_output.identifier support. Resolves #602. --- include/bar.hpp | 1 + include/client.hpp | 15 ++++++++------- src/client.cpp | 48 ++++++++++++++++++++++++++++------------------ 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 8aab8f7..d6cd895 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -17,6 +17,7 @@ class Factory; struct waybar_output { Glib::RefPtr monitor; std::string name; + std::string identifier; std::unique_ptr xdg_output = { nullptr, &zxdg_output_v1_destroy}; diff --git a/include/client.hpp b/include/client.hpp index 05215cc..f533942 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -6,6 +6,7 @@ #include #include #include + #include "bar.hpp" struct zwlr_layer_shell_v1; @@ -33,18 +34,18 @@ class Client { 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(struct waybar_output &output); - bool isValidOutput(const Json::Value &config, struct waybar_output &output); - auto setupConfig(const std::string &config_file) -> void; - auto setupCss(const std::string &css_file) -> void; - struct waybar_output &getOutput(void *); + const std::string getValidPath(const std::vector &paths) const; + void handleOutput(struct waybar_output &output); + bool isValidOutput(const Json::Value &config, struct waybar_output &output); + auto setupConfig(const std::string &config_file) -> void; + auto setupCss(const std::string &css_file) -> void; + struct waybar_output & getOutput(void *); std::vector getOutputConfigs(struct waybar_output &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 *); + static void handleOutputDescription(void *, struct zxdg_output_v1 *, const char *); void handleMonitorAdded(Glib::RefPtr monitor); void handleMonitorRemoved(Glib::RefPtr monitor); diff --git a/src/client.cpp b/src/client.cpp index 005761e..627904c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,12 +1,14 @@ #include "client.hpp" + #include #include + #include #include -#include "util/clara.hpp" -#include "util/json.hpp" #include "idle-inhibit-unstable-v1-client-protocol.h" +#include "util/clara.hpp" +#include "util/json.hpp" #include "wlr-layer-shell-unstable-v1-client-protocol.h" waybar::Client *waybar::Client::inst() { @@ -59,8 +61,8 @@ void waybar::Client::handleOutput(struct waybar_output &output) { .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 *) {}, + .name = [](void *, struct zxdg_output_v1 *, const char *) {}, + .description = &handleOutputDescription, }; // owned by output->monitor; no need to destroy auto wl_output = gdk_wayland_monitor_get_wl_output(output.monitor->gobj()); @@ -71,18 +73,21 @@ void waybar::Client::handleOutput(struct waybar_output &output) { bool waybar::Client::isValidOutput(const Json::Value &config, struct waybar_output &output) { if (config["output"].isArray()) { for (auto const &output_conf : config["output"]) { - if (output_conf.isString() && output_conf.asString() == output.name) { + if (output_conf.isString() && + (output_conf.asString() == output.name || output_conf.asString() == output.identifier)) { + std::cout << output_conf.asString() << std::endl; return true; } } return false; } else if (config["output"].isString()) { - auto config_output_name = config["output"].asString(); - if (!config_output_name.empty()) { - if (config_output_name.substr(0, 1) == "!") { - return config_output_name.substr(1) != output.name; + auto config_output = config["output"].asString(); + if (!config_output.empty()) { + if (config_output.substr(0, 1) == "!") { + return config_output.substr(1) != output.name || + config_output.substr(1) != output.identifier; } - return config_output_name == output.name; + return config_output == output.name || config_output == output.identifier; } } @@ -112,16 +117,20 @@ std::vector waybar::Client::getOutputConfigs(struct waybar_output & return configs; } -void waybar::Client::handleOutputName(void * data, struct zxdg_output_v1 * /*xdg_output*/, - const char *name) { +void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * /*xdg_output*/, + const char *description) { auto client = waybar::Client::inst(); try { - auto &output = client->getOutput(data); - output.name = name; - spdlog::debug("Output detected: {} ({} {})", - name, - output.monitor->get_manufacturer(), - output.monitor->get_model()); + auto & output = client->getOutput(data); + const char *open_paren = strrchr(description, '('); + const char *close_paren = strrchr(description, ')'); + + // Description format: "identifier (name)" + size_t identifier_length = open_paren - description; + output.identifier = std::string(description, identifier_length - 1); + output.name = std::string(description + identifier_length + 1, close_paren - open_paren - 1); + + spdlog::debug("Output detected: {}", description); auto configs = client->getOutputConfigs(output); if (configs.empty()) { output.xdg_output.reset(); @@ -260,7 +269,8 @@ int waybar::Client::main(int argc, char *argv[]) { if (!log_level.empty()) { spdlog::set_level(spdlog::level::from_str(log_level)); } - gtk_app = Gtk::Application::create(argc, argv, "fr.arouillard.waybar", Gio::APPLICATION_HANDLES_COMMAND_LINE); + gtk_app = Gtk::Application::create( + argc, argv, "fr.arouillard.waybar", Gio::APPLICATION_HANDLES_COMMAND_LINE); gdk_display = Gdk::Display::get_default(); if (!gdk_display) { throw std::runtime_error("Can't find display"); From e5684c6127d2fb063a7171a35cd52e03d5187ab3 Mon Sep 17 00:00:00 2001 From: Andreas Backx Date: Fri, 25 Dec 2020 23:03:01 +0000 Subject: [PATCH 154/355] Separated name and description setup and moved bar creation to done callback. --- include/client.hpp | 2 ++ src/client.cpp | 51 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index f533942..f2eafb1 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -45,6 +45,8 @@ class Client { 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 handleOutputDone(void *, struct zxdg_output_v1 *); + static void handleOutputName(void *, struct zxdg_output_v1 *, const char *); static void handleOutputDescription(void *, struct zxdg_output_v1 *, const char *); void handleMonitorAdded(Glib::RefPtr monitor); void handleMonitorRemoved(Glib::RefPtr monitor); diff --git a/src/client.cpp b/src/client.cpp index 627904c..4240f09 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -60,8 +60,8 @@ void waybar::Client::handleOutput(struct waybar_output &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 = [](void *, struct zxdg_output_v1 *, const char *) {}, + .done = &handleOutputDone, + .name = &handleOutputName, .description = &handleOutputDescription, }; // owned by output->monitor; no need to destroy @@ -75,7 +75,6 @@ bool waybar::Client::isValidOutput(const Json::Value &config, struct waybar_outp for (auto const &output_conf : config["output"]) { if (output_conf.isString() && (output_conf.asString() == output.name || output_conf.asString() == output.identifier)) { - std::cout << output_conf.asString() << std::endl; return true; } } @@ -84,7 +83,7 @@ bool waybar::Client::isValidOutput(const Json::Value &config, struct waybar_outp auto config_output = config["output"].asString(); if (!config_output.empty()) { if (config_output.substr(0, 1) == "!") { - return config_output.substr(1) != output.name || + return config_output.substr(1) != output.name && config_output.substr(1) != output.identifier; } return config_output == output.name || config_output == output.identifier; @@ -117,20 +116,12 @@ std::vector waybar::Client::getOutputConfigs(struct waybar_output & return configs; } -void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * /*xdg_output*/, - const char *description) { +void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_output*/) { auto client = waybar::Client::inst(); try { - auto & output = client->getOutput(data); - const char *open_paren = strrchr(description, '('); - const char *close_paren = strrchr(description, ')'); + auto &output = client->getOutput(data); + spdlog::debug("Output detection done: {} ({})", output.name, output.identifier); - // Description format: "identifier (name)" - size_t identifier_length = open_paren - description; - output.identifier = std::string(description, identifier_length - 1); - output.name = std::string(description + identifier_length + 1, close_paren - open_paren - 1); - - spdlog::debug("Output detected: {}", description); auto configs = client->getOutputConfigs(output); if (configs.empty()) { output.xdg_output.reset(); @@ -148,6 +139,36 @@ void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * } } +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); + spdlog::debug("Output detected with name: {} ({} {})", + name, + output.monitor->get_manufacturer(), + output.monitor->get_model()); + output.name = name; + } catch (const std::exception &e) { + std::cerr << e.what() << std::endl; + } +} + +void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * /*xdg_output*/, + const char *description) { + auto client = waybar::Client::inst(); + try { + auto & output = client->getOutput(data); + const char *open_paren = strrchr(description, '('); + + // Description format: "identifier (name)" + size_t identifier_length = open_paren - description; + output.identifier = std::string(description, identifier_length - 1); + } catch (const std::exception &e) { + std::cerr << e.what() << std::endl; + } +} + void waybar::Client::handleMonitorAdded(Glib::RefPtr monitor) { auto &output = outputs_.emplace_back(); output.monitor = monitor; From 3fbbbf85416cb6755cd0b6fb81f84e8938b58658 Mon Sep 17 00:00:00 2001 From: Andreas Backx Date: Fri, 25 Dec 2020 23:31:29 +0000 Subject: [PATCH 155/355] Removed redundant log line. --- src/client.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 4240f09..ad281d5 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -144,10 +144,6 @@ void waybar::Client::handleOutputName(void * data, struct zxdg_output_v1 * auto client = waybar::Client::inst(); try { auto &output = client->getOutput(data); - spdlog::debug("Output detected with name: {} ({} {})", - name, - output.monitor->get_manufacturer(), - output.monitor->get_model()); output.name = name; } catch (const std::exception &e) { std::cerr << e.what() << std::endl; From c0361e8546213d8d4653859329a7d02da4f56191 Mon Sep 17 00:00:00 2001 From: dorgnarg Date: Mon, 28 Dec 2020 13:34:59 -0700 Subject: [PATCH 156/355] A hopeful fix to the module section classes when the bar is vertical --- src/bar.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bar.cpp b/src/bar.cpp index 7bd6172..10cf0fc 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -387,6 +387,9 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); box_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); + left_.get_style_context()->add_class("modules-left"); + center_.get_style_context()->add_class("modules-center"); + right_.get_style_context()->add_class("modules-right"); vertical = true; } From 42e86677739a62abaeb078cfdaed773e6ce03eea Mon Sep 17 00:00:00 2001 From: dorgnarg Date: Mon, 28 Dec 2020 13:44:16 -0700 Subject: [PATCH 157/355] Better way of doing it by just moving the original delcarations after everything's for sure been set --- src/bar.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index 10cf0fc..1dbd69a 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -370,9 +370,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) window.get_style_context()->add_class(output->name); window.get_style_context()->add_class(config["name"].asString()); window.get_style_context()->add_class(config["position"].asString()); - left_.get_style_context()->add_class("modules-left"); - center_.get_style_context()->add_class("modules-center"); - right_.get_style_context()->add_class("modules-right"); if (config["layer"] == "top") { layer_ = bar_layer::TOP; @@ -387,11 +384,12 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); box_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); - left_.get_style_context()->add_class("modules-left"); - center_.get_style_context()->add_class("modules-center"); - right_.get_style_context()->add_class("modules-right"); vertical = true; } + + left_.get_style_context()->add_class("modules-left"); + center_.get_style_context()->add_class("modules-center"); + right_.get_style_context()->add_class("modules-right"); uint32_t height = config["height"].isUInt() ? config["height"].asUInt() : 0; uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0; From 00046d309d25c29d8b339475c028685b0dafb221 Mon Sep 17 00:00:00 2001 From: ocisra Date: Sun, 3 Jan 2021 15:25:19 +0100 Subject: [PATCH 158/355] add an option to use battery design capacity as a reference for percentage informations --- man/waybar-battery.5.scd | 5 +++++ src/modules/battery.cpp | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index c9e6e78..869df32 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -22,6 +22,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y typeof: integer ++ Define the max percentage of the battery, for when you've set the battery to stop charging at a lower level to save it. For example, if you've set the battery to stop at 80% that will become the new 100%. +*design-capacity*: ++ + typeof: bool ++ + default: false ++ + Option to use the battery design capacity instead of it's current maximal capacity. + *interval*: ++ typeof: integer ++ default: 60 ++ diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 0b54e9c..daf113c 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -142,12 +142,14 @@ const std::tuple waybar::modules::Battery::getInfos uint32_t total_power = 0; // μW uint32_t total_energy = 0; // μWh uint32_t total_energy_full = 0; + uint32_t total_energy_full_design = 0; std::string status = "Unknown"; for (auto const& item : batteries_) { auto bat = item.first; uint32_t power_now; uint32_t energy_full; uint32_t energy_now; + uint32_t energy_full_design; std::string _status; std::ifstream(bat / "status") >> _status; auto rate_path = fs::exists(bat / "current_now") ? "current_now" : "power_now"; @@ -156,17 +158,20 @@ const std::tuple waybar::modules::Battery::getInfos 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; + auto full_design_path = fs::exists(bat / "charge_full_design") ? "charge_full_design" : "energy_full_design"; + std::ifstream(bat / full_design_path) >> energy_full_design; if (_status != "Unknown") { status = _status; } total_power += power_now; total_energy += energy_now; total_energy_full += energy_full; + total_energy_full_design += energy_full_design; } if (!adapter_.empty() && status == "Discharging") { bool online; std::ifstream(adapter_ / "online") >> online; - if (online) { + if (online) { status = "Plugged"; } } @@ -182,6 +187,10 @@ const std::tuple waybar::modules::Battery::getInfos } } float capacity = ((float)total_energy * 100.0f / (float) total_energy_full); + // Handle design-capacity + if (config_["design-capacity"].isBool() ? config_["design-capacity"].asBool() : false) { + capacity = ((float)total_energy * 100.0f / (float) total_energy_full_design); + } // Handle full-at if (config_["full-at"].isUInt()) { auto full_at = config_["full-at"].asUInt(); From f20dbbbd74316917b0541c7b64aa29f3d58d66ee Mon Sep 17 00:00:00 2001 From: Johannes Christenson Date: Sun, 3 Jan 2021 19:08:06 +0100 Subject: [PATCH 159/355] Fixing logic in getIcon --- src/ALabel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 3a4063d..9371a0e 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -60,14 +60,14 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_ std::string ALabel::getIcon(uint16_t percentage, std::vector& alts, uint16_t max) { auto format_icons = config_["format-icons"]; if (format_icons.isObject()) { + std::string _alt = "default"; for (const auto& alt : alts) { if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) { - format_icons = format_icons[alt]; + _alt = alt; break; - } else { - format_icons = format_icons["default"]; } } + format_icons = format_icons[_alt]; } if (format_icons.isArray()) { auto size = format_icons.size(); From ef9c3ef1cbb57afa2f64a70282fffe33e5519dfe Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 6 Jan 2021 07:03:14 -0800 Subject: [PATCH 160/355] fix(wlr/taskbar): fix wl_array out-of-bounds access wl_array->size contains the number of bytes in the array instead of the number of elements. --- src/modules/wlr/taskbar.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index e70ad9c..46147bd 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -367,16 +367,16 @@ void Task::handle_output_leave(struct wl_output *output) void Task::handle_state(struct wl_array *state) { state_ = 0; - for (auto* entry = static_cast(state->data); - entry < static_cast(state->data) + state->size; - entry++) { - if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) + size_t size = state->size / sizeof(uint32_t); + for (size_t i = 0; i < size; ++i) { + auto entry = static_cast(state->data)[i]; + if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) state_ |= MAXIMIZED; - if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) + if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) state_ |= MINIMIZED; - if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) + if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) state_ |= ACTIVE; - if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) + if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) state_ |= FULLSCREEN; } } From b79301a5bdafb2d0a7764990530898996a97c386 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 8 Jan 2021 15:07:04 -0800 Subject: [PATCH 161/355] fix(wlr/taskbar): protocol error when reconnecting outputs Destroy request is not specified for foreign toplevel manager and it does not prevent the compositor from sending more events. Libwayland would ignore events to a destroyed objects, but that could indirectly cause a gap in the sequence of new object ids and trigger error condition in the library. With this commit waybar sends a `stop` request to notify the compositor about the destruction of a toplevel manager. That fixes abnormal termination of the bar with following errors: ``` (waybar:11791): Gdk-DEBUG: 20:04:19.778: not a valid new object id (4278190088), message toplevel(n) Gdk-Message: 20:04:19.778: Error reading events from display: Invalid argument ``` --- src/modules/wlr/taskbar.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 46147bd..4cbb8ce 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -637,8 +637,20 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu Taskbar::~Taskbar() { if (manager_) { - zwlr_foreign_toplevel_manager_v1_destroy(manager_); - manager_ = nullptr; + struct wl_display *display = Client::inst()->wl_display; + /* + * Send `stop` request and wait for one roundtrip. + * This is not quite correct as the protocol encourages us to wait for the .finished event, + * but it should work with wlroots foreign toplevel manager implementation. + */ + zwlr_foreign_toplevel_manager_v1_stop(manager_); + wl_display_roundtrip(display); + + if (manager_) { + spdlog::warn("Foreign toplevel manager destroyed before .finished event"); + zwlr_foreign_toplevel_manager_v1_destroy(manager_); + manager_ = nullptr; + } } } From f4ffb21c8c364eea6589f88dbf49d7661d2f07a6 Mon Sep 17 00:00:00 2001 From: Kamus Hadenes Date: Tue, 12 Jan 2021 18:51:44 -0300 Subject: [PATCH 162/355] improve sink/source separation Add additional fields, namely `source_volume` and `source_desc` Add `tooltip-format`, reverting to default behavior if not specified Add additional CSS classes, namely `sink-muted` and `source-muted` --- src/modules/pulseaudio.cpp | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 571a78e..72327fe 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -207,6 +207,7 @@ const std::string waybar::modules::Pulseaudio::getPortIcon() const { auto waybar::modules::Pulseaudio::update() -> void { auto format = format_; + std::string tooltip_format; if (!alt_) { std::string format_name = "format"; if (monitor_.find("a2dp_sink") != std::string::npos) { @@ -222,28 +223,53 @@ auto waybar::modules::Pulseaudio::update() -> void { } format_name = format_name + "-muted"; label_.get_style_context()->add_class("muted"); + label_.get_style_context()->add_class("sink-muted"); } else { label_.get_style_context()->remove_class("muted"); + label_.get_style_context()->remove_class("sink-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(); + if (source_muted_) { + label_.get_style_context()->add_class("source-muted"); + if (config_["format-source-muted"].isString()) { + format_source = config_["format-source-muted"].asString(); + } + } else { + label_.get_style_context()->remove_class("source-muted"); + if (config_["format-source-muted"].isString()) { + format_source = config_["format-source"].asString(); + } } format_source = fmt::format(format_source, fmt::arg("volume", source_volume_)); label_.set_markup(fmt::format(format, fmt::arg("desc", desc_), fmt::arg("volume", volume_), fmt::arg("format_source", format_source), + fmt::arg("source_volume", source_volume_), + fmt::arg("source_desc", source_desc_), fmt::arg("icon", getIcon(volume_, getPortIcon())))); getState(volume_); + if (tooltipEnabled()) { - label_.set_tooltip_text(desc_); + if (tooltip_format.empty() && config_["tooltip-format"].isString()) { + tooltip_format = config_["tooltip-format"].asString(); + } + if (!tooltip_format.empty()) { + label_.set_tooltip_text(fmt::format( + tooltip_format, + fmt::arg("desc", desc_), + fmt::arg("volume", volume_), + fmt::arg("format_source", format_source), + fmt::arg("source_volume", source_volume_), + fmt::arg("source_desc", source_desc_), + fmt::arg("icon", getIcon(volume_, getPortIcon()))); + } else { + label_.set_tooltip_text(desc_); + } } // Call parent update From a7941a00c59035415066acf387822322982a3238 Mon Sep 17 00:00:00 2001 From: Kamus Hadenes Date: Tue, 12 Jan 2021 19:10:34 -0300 Subject: [PATCH 163/355] fix missing parentheses --- src/modules/pulseaudio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 72327fe..7f4f3b6 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -266,7 +266,7 @@ auto waybar::modules::Pulseaudio::update() -> void { fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_), fmt::arg("source_desc", source_desc_), - fmt::arg("icon", getIcon(volume_, getPortIcon()))); + fmt::arg("icon", getIcon(volume_, getPortIcon())))); } else { label_.set_tooltip_text(desc_); } From 9d5ce45f3b7571d10d19ee122f9739c6b3d04e40 Mon Sep 17 00:00:00 2001 From: sjtio Date: Fri, 15 Jan 2021 01:07:56 +0000 Subject: [PATCH 164/355] add option tag-labels to river/tags --- man/waybar-river-tags.5.scd | 4 ++++ src/modules/river/tags.cpp | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/man/waybar-river-tags.5.scd b/man/waybar-river-tags.5.scd index a02ddeb..0f02724 100644 --- a/man/waybar-river-tags.5.scd +++ b/man/waybar-river-tags.5.scd @@ -17,6 +17,10 @@ Addressed by *river/tags* default: 9 ++ The number of tags that should be displayed. +*tag-labels*: ++ + typeof: array ++ + The label to display for each tag. + # EXAMPLE ``` diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index 804ea09..e96b201 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "client.hpp" #include "modules/river/tags.hpp" #include "river-status-unstable-v1-client-protocol.h" @@ -64,8 +66,20 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con // Default to 9 tags const uint32_t num_tags = config["num-tags"].isUInt() ? config_["num-tags"].asUInt() : 9; - for (uint32_t tag = 1; tag <= num_tags; ++tag) { - Gtk::Button &button = buttons_.emplace_back(std::to_string(tag)); + + std::vector tag_labels(num_tags); + for (uint32_t tag = 0; tag < num_tags; ++tag) { + tag_labels[tag] = std::to_string(tag+1); + } + const Json::Value custom_labels = config["tag-labels"]; + if (custom_labels.isArray() && !custom_labels.empty()) { + for (uint32_t tag = 0; tag < std::min(num_tags, custom_labels.size()); ++tag) { + tag_labels[tag] = custom_labels[tag].asString(); + } + } + + for (const auto &tag_label : tag_labels) { + Gtk::Button &button = buttons_.emplace_back(tag_label); button.set_relief(Gtk::RELIEF_NONE); box_.pack_start(button, false, false, 0); button.show(); From ce0bf6269b3b75ccc3a37792e1b4065cd15b98a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C4=8Cernoch?= Date: Mon, 18 Jan 2021 12:32:51 +0100 Subject: [PATCH 165/355] battery: use timeTo as the default format name --- src/modules/battery.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index e0f9aca..8b86212 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -253,7 +253,7 @@ auto waybar::modules::Battery::update() -> void { auto time_remaining_formatted = formatTimeRemaining(time_remaining); if (tooltipEnabled()) { std::string tooltip_text_default; - std::string tooltip_format = "{autoTooltip}"; + std::string tooltip_format = "{timeTo}"; if (time_remaining != 0) { std::string time_to = std::string("Time to ") + ((time_remaining > 0) ? "empty" : "full"); tooltip_text_default = time_to + ": " + time_remaining_formatted; @@ -270,7 +270,7 @@ auto waybar::modules::Battery::update() -> void { tooltip_format = config_["tooltip-format"].asString(); } label_.set_tooltip_text(fmt::format(tooltip_format, - fmt::arg("autoTooltip", tooltip_text_default), + fmt::arg("timeTo", tooltip_text_default), fmt::arg("capacity", capacity), fmt::arg("time", time_remaining_formatted))); } From 0bd96f339eadca58142e694732ac2381cc5ea9b7 Mon Sep 17 00:00:00 2001 From: ocisra Date: Mon, 18 Jan 2021 12:38:02 +0100 Subject: [PATCH 166/355] typo --- .../index/ALabel.cpp.09E9F4748A3CBF6C.idx | Bin 0 -> 3224 bytes .../index/ALabel.hpp.81F9065828AA88EC.idx | Bin 0 -> 1438 bytes .../index/AModule.cpp.8C79E6793B0ABDDC.idx | Bin 0 -> 3206 bytes .../index/AModule.hpp.D8753B04060BBA88.idx | Bin 0 -> 1730 bytes .../index/IModule.hpp.E1245342A901572D.idx | Bin 0 -> 482 bytes .../index/backlight.cpp.8F986B9BECE3CA9B.idx | Bin 0 -> 5394 bytes .../index/backlight.hpp.0EF1448C3968172E.idx | Bin 0 -> 2104 bytes .../clangd/index/bar.cpp.499C72A404C3B6E0.idx | Bin 0 -> 11056 bytes .../clangd/index/bar.hpp.548C657FFF81C379.idx | Bin 0 -> 3490 bytes .../index/battery.cpp.C1194A004F38A6C5.idx | Bin 0 -> 5698 bytes .../index/battery.hpp.A5CF2192CCD687A2.idx | Bin 0 -> 1704 bytes .../clangd/index/clara.hpp.37039B002C851AB7.idx | Bin 0 -> 30604 bytes .../index/client.cpp.142786E5739C6086.idx | Bin 0 -> 3324 bytes .../index/client.cpp.90DC0642EC0F3846.idx | Bin 0 -> 7640 bytes .../index/client.hpp.B4D0F538B28EFA9A.idx | Bin 0 -> 1852 bytes .../index/client.hpp.F7B62724EF944420.idx | Bin 0 -> 2630 bytes .../clangd/index/clock.cpp.622BF041106573C2.idx | Bin 0 -> 4824 bytes .../clangd/index/clock.hpp.6C252B8E1A974E1B.idx | Bin 0 -> 1528 bytes .../index/command.hpp.11D8330C13D77545.idx | Bin 0 -> 2266 bytes .../index/common.cpp.8896DC31B9E7EE81.idx | Bin 0 -> 2080 bytes .../index/common.cpp.FA1CA1F6B4D16C67.idx | Bin 0 -> 1866 bytes .../clangd/index/cpu.hpp.A6F11840DCF8A513.idx | Bin 0 -> 1026 bytes .../index/custom.cpp.EA9CE9AF81C1E5D4.idx | Bin 0 -> 4732 bytes .../index/custom.hpp.36F2F4DF290D9AA1.idx | Bin 0 -> 1720 bytes .../clangd/index/disk.cpp.1F7580C201D672A2.idx | Bin 0 -> 1998 bytes .../clangd/index/disk.hpp.E57994872D8447F8.idx | Bin 0 -> 728 bytes .../index/factory.cpp.CEDE1C1627112064.idx | Bin 0 -> 2394 bytes .../index/factory.hpp.2AEC37A00908E370.idx | Bin 0 -> 904 bytes .../index/format.hpp.A472BFB350A64B30.idx | Bin 0 -> 964 bytes .../clangd/index/host.cpp.0C1CF26FC798A71A.idx | Bin 0 -> 4080 bytes .../clangd/index/host.hpp.D6E6FF9FFAF571DF.idx | Bin 0 -> 1978 bytes .../idle_inhibitor.cpp.3D718BD05B870FA9.idx | Bin 0 -> 2398 bytes .../idle_inhibitor.hpp.A4AFAA5C5DDDE471.idx | Bin 0 -> 898 bytes .../clangd/index/ipc.hpp.804BDBBDF260032D.idx | Bin 0 -> 1498 bytes .../clangd/index/item.cpp.BDF1AC58410539C1.idx | Bin 0 -> 7280 bytes .../clangd/index/item.hpp.CF2C10DA19A462FB.idx | Bin 0 -> 3008 bytes .../clangd/index/json.hpp.6C08A0DAD19BC4D8.idx | Bin 0 -> 750 bytes .../index/language.cpp.7CFC0E2AB711785B.idx | Bin 0 -> 2412 bytes .../index/language.hpp.045E99AD59170347.idx | Bin 0 -> 954 bytes .../clangd/index/linux.cpp.AA11E43948BF7636.idx | Bin 0 -> 1320 bytes .../clangd/index/linux.cpp.EBDA54C079A9D2C4.idx | Bin 0 -> 1528 bytes .../clangd/index/main.cpp.9D6EE073CE3F67E9.idx | Bin 0 -> 2136 bytes .../index/memory.hpp.220BFCF008454788.idx | Bin 0 -> 740 bytes .../clangd/index/mode.cpp.DEC43BA6A32D0056.idx | Bin 0 -> 2090 bytes .../clangd/index/mode.hpp.6A926FBEE534F2A9.idx | Bin 0 -> 888 bytes .../clangd/index/mpd.cpp.7FCBEF52ABE61287.idx | Bin 0 -> 6492 bytes .../clangd/index/mpd.hpp.F92558038735ED47.idx | Bin 0 -> 2124 bytes .../index/network.cpp.109ECEBB28F3CA1E.idx | Bin 0 -> 12826 bytes .../index/network.hpp.959179E628BFA829.idx | Bin 0 -> 3296 bytes .../index/pulseaudio.cpp.33560C8DDD3A5AD3.idx | Bin 0 -> 5832 bytes .../index/pulseaudio.hpp.C6D74738A7A6B198.idx | Bin 0 -> 2202 bytes .../sleeper_thread.hpp.B273FAC75439EB17.idx | Bin 0 -> 1672 bytes .../clangd/index/sndio.cpp.1174277772D16F52.idx | Bin 0 -> 3834 bytes .../clangd/index/sndio.hpp.2C5698C31C5CA7B4.idx | Bin 0 -> 1304 bytes .../clangd/index/state.cpp.E5F8B9AFD9E7ED3F.idx | Bin 0 -> 7084 bytes .../clangd/index/state.hpp.210A97315D520642.idx | Bin 0 -> 7032 bytes .../index/state.inl.hpp.07C5BE693644AFA7.idx | Bin 0 -> 1336 bytes .../clangd/index/tags.cpp.901229A9EA5F0AD7.idx | Bin 0 -> 3004 bytes .../clangd/index/tags.hpp.38EBC6B49867D962.idx | Bin 0 -> 1076 bytes .../index/taskbar.cpp.3B871EDA6D279756.idx | Bin 0 -> 12886 bytes .../index/taskbar.hpp.3F1105A3E0CB852D.idx | Bin 0 -> 5366 bytes .../index/temperature.cpp.BB5C0ED5BD80EEFE.idx | Bin 0 -> 2538 bytes .../index/temperature.hpp.58F6012FF8EB97C4.idx | Bin 0 -> 818 bytes .../clangd/index/tray.cpp.02A8D890AD31BCCD.idx | Bin 0 -> 2114 bytes .../clangd/index/tray.hpp.469C1AF36DEA64C2.idx | Bin 0 -> 988 bytes .../index/watcher.cpp.793479CFFB135BF1.idx | Bin 0 -> 4334 bytes .../index/watcher.hpp.F4849F54352399E5.idx | Bin 0 -> 2232 bytes .../index/window.cpp.8AB8664D31F55915.idx | Bin 0 -> 3582 bytes .../index/window.hpp.E69A5ADAD0A0E8F9.idx | Bin 0 -> 1456 bytes .../index/workspaces.cpp.EC0CB7DF82BEDBA3.idx | Bin 0 -> 7454 bytes .../index/workspaces.hpp.0FDE443B68162423.idx | Bin 0 -> 2044 bytes src/modules/battery.cpp | 2 +- 72 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 .cache/clangd/index/ALabel.cpp.09E9F4748A3CBF6C.idx create mode 100644 .cache/clangd/index/ALabel.hpp.81F9065828AA88EC.idx create mode 100644 .cache/clangd/index/AModule.cpp.8C79E6793B0ABDDC.idx create mode 100644 .cache/clangd/index/AModule.hpp.D8753B04060BBA88.idx create mode 100644 .cache/clangd/index/IModule.hpp.E1245342A901572D.idx create mode 100644 .cache/clangd/index/backlight.cpp.8F986B9BECE3CA9B.idx create mode 100644 .cache/clangd/index/backlight.hpp.0EF1448C3968172E.idx create mode 100644 .cache/clangd/index/bar.cpp.499C72A404C3B6E0.idx create mode 100644 .cache/clangd/index/bar.hpp.548C657FFF81C379.idx create mode 100644 .cache/clangd/index/battery.cpp.C1194A004F38A6C5.idx create mode 100644 .cache/clangd/index/battery.hpp.A5CF2192CCD687A2.idx create mode 100644 .cache/clangd/index/clara.hpp.37039B002C851AB7.idx create mode 100644 .cache/clangd/index/client.cpp.142786E5739C6086.idx create mode 100644 .cache/clangd/index/client.cpp.90DC0642EC0F3846.idx create mode 100644 .cache/clangd/index/client.hpp.B4D0F538B28EFA9A.idx create mode 100644 .cache/clangd/index/client.hpp.F7B62724EF944420.idx create mode 100644 .cache/clangd/index/clock.cpp.622BF041106573C2.idx create mode 100644 .cache/clangd/index/clock.hpp.6C252B8E1A974E1B.idx create mode 100644 .cache/clangd/index/command.hpp.11D8330C13D77545.idx create mode 100644 .cache/clangd/index/common.cpp.8896DC31B9E7EE81.idx create mode 100644 .cache/clangd/index/common.cpp.FA1CA1F6B4D16C67.idx create mode 100644 .cache/clangd/index/cpu.hpp.A6F11840DCF8A513.idx create mode 100644 .cache/clangd/index/custom.cpp.EA9CE9AF81C1E5D4.idx create mode 100644 .cache/clangd/index/custom.hpp.36F2F4DF290D9AA1.idx create mode 100644 .cache/clangd/index/disk.cpp.1F7580C201D672A2.idx create mode 100644 .cache/clangd/index/disk.hpp.E57994872D8447F8.idx create mode 100644 .cache/clangd/index/factory.cpp.CEDE1C1627112064.idx create mode 100644 .cache/clangd/index/factory.hpp.2AEC37A00908E370.idx create mode 100644 .cache/clangd/index/format.hpp.A472BFB350A64B30.idx create mode 100644 .cache/clangd/index/host.cpp.0C1CF26FC798A71A.idx create mode 100644 .cache/clangd/index/host.hpp.D6E6FF9FFAF571DF.idx create mode 100644 .cache/clangd/index/idle_inhibitor.cpp.3D718BD05B870FA9.idx create mode 100644 .cache/clangd/index/idle_inhibitor.hpp.A4AFAA5C5DDDE471.idx create mode 100644 .cache/clangd/index/ipc.hpp.804BDBBDF260032D.idx create mode 100644 .cache/clangd/index/item.cpp.BDF1AC58410539C1.idx create mode 100644 .cache/clangd/index/item.hpp.CF2C10DA19A462FB.idx create mode 100644 .cache/clangd/index/json.hpp.6C08A0DAD19BC4D8.idx create mode 100644 .cache/clangd/index/language.cpp.7CFC0E2AB711785B.idx create mode 100644 .cache/clangd/index/language.hpp.045E99AD59170347.idx create mode 100644 .cache/clangd/index/linux.cpp.AA11E43948BF7636.idx create mode 100644 .cache/clangd/index/linux.cpp.EBDA54C079A9D2C4.idx create mode 100644 .cache/clangd/index/main.cpp.9D6EE073CE3F67E9.idx create mode 100644 .cache/clangd/index/memory.hpp.220BFCF008454788.idx create mode 100644 .cache/clangd/index/mode.cpp.DEC43BA6A32D0056.idx create mode 100644 .cache/clangd/index/mode.hpp.6A926FBEE534F2A9.idx create mode 100644 .cache/clangd/index/mpd.cpp.7FCBEF52ABE61287.idx create mode 100644 .cache/clangd/index/mpd.hpp.F92558038735ED47.idx create mode 100644 .cache/clangd/index/network.cpp.109ECEBB28F3CA1E.idx create mode 100644 .cache/clangd/index/network.hpp.959179E628BFA829.idx create mode 100644 .cache/clangd/index/pulseaudio.cpp.33560C8DDD3A5AD3.idx create mode 100644 .cache/clangd/index/pulseaudio.hpp.C6D74738A7A6B198.idx create mode 100644 .cache/clangd/index/sleeper_thread.hpp.B273FAC75439EB17.idx create mode 100644 .cache/clangd/index/sndio.cpp.1174277772D16F52.idx create mode 100644 .cache/clangd/index/sndio.hpp.2C5698C31C5CA7B4.idx create mode 100644 .cache/clangd/index/state.cpp.E5F8B9AFD9E7ED3F.idx create mode 100644 .cache/clangd/index/state.hpp.210A97315D520642.idx create mode 100644 .cache/clangd/index/state.inl.hpp.07C5BE693644AFA7.idx create mode 100644 .cache/clangd/index/tags.cpp.901229A9EA5F0AD7.idx create mode 100644 .cache/clangd/index/tags.hpp.38EBC6B49867D962.idx create mode 100644 .cache/clangd/index/taskbar.cpp.3B871EDA6D279756.idx create mode 100644 .cache/clangd/index/taskbar.hpp.3F1105A3E0CB852D.idx create mode 100644 .cache/clangd/index/temperature.cpp.BB5C0ED5BD80EEFE.idx create mode 100644 .cache/clangd/index/temperature.hpp.58F6012FF8EB97C4.idx create mode 100644 .cache/clangd/index/tray.cpp.02A8D890AD31BCCD.idx create mode 100644 .cache/clangd/index/tray.hpp.469C1AF36DEA64C2.idx create mode 100644 .cache/clangd/index/watcher.cpp.793479CFFB135BF1.idx create mode 100644 .cache/clangd/index/watcher.hpp.F4849F54352399E5.idx create mode 100644 .cache/clangd/index/window.cpp.8AB8664D31F55915.idx create mode 100644 .cache/clangd/index/window.hpp.E69A5ADAD0A0E8F9.idx create mode 100644 .cache/clangd/index/workspaces.cpp.EC0CB7DF82BEDBA3.idx create mode 100644 .cache/clangd/index/workspaces.hpp.0FDE443B68162423.idx diff --git a/.cache/clangd/index/ALabel.cpp.09E9F4748A3CBF6C.idx b/.cache/clangd/index/ALabel.cpp.09E9F4748A3CBF6C.idx new file mode 100644 index 0000000000000000000000000000000000000000..ce9ae3c77ff53668450d36c7f728fc9387c484cc GIT binary patch literal 3224 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/ALabel.hpp.81F9065828AA88EC.idx b/.cache/clangd/index/ALabel.hpp.81F9065828AA88EC.idx new file mode 100644 index 0000000000000000000000000000000000000000..317b3e5b9a87c3e2caa4c3994b6785c740ecce84 GIT binary patch literal 1438 zcmYk6e^3-v0LR~3-f_3L?{436cYED+KOzh=usE+M^%CLm2I9vVn8^<*PfjKVWZ*1M zb275ysFAZWHKA#YF&TBxrpAd8b%-F+GBGq9oB$gY&4{3}C`%ib*@yebciw!yv-^I0 zc3ys#$FoZTfV(U!Smj^88UX+_0D!>y+Lt;20M9`Hf*&@%lgoD{tLkruEuN>xuAR&9 zHU&#Ao*rB{_~@!b6F*mPc;Va!zm6zMN9urYOZR&#ALvcbJm|R9^5>U6`#}9%B^aAm zcO=J14o@XHUav7EWF;RhKUQ_O#akM`-|l*O_?WMtsAI6KqwtNMzI`eE-v%o;ZE9{y zP`JiE`(|k2xW{>Z^tQgoef6i2vqBu(m^Xjb@%)^w_RPzXl7^afvH6M0>yDxKN9~UP z_Ii(BD?bUF2VUs~fsIwZ76^cgSC%GM3h7kY~tzxVcKsCKt0Dx&M_xV5YiFPd$ zB>-f!8k3};eOlbsPnT>Tze5mBLXL86|AS>xe|0iYhl^vJT6c}L^3mEY> zJ{}`}DW8iGv5|O;NG4e(%Ytf2lI#YmB_{>!(i2sF5>aEs&E+1$h+E8MV8j)0g&6TX zZ@`Fm@(VB`JTYKI7LZho^h&)3Bcsx&!N|;;4a@Ul60X1NQb3HzJmQq~LRQi$+k~uS zvC6elw|HmK*AOF}Qio-iP%SwOPj%l|{_J0GLX6CIbG&R4GLpd^AHC{)yR$O{G2#rI zS@sB7$zQu?XYlZn(TflvK8<&G|1kPXYxtB4Vq`EFEV9%nwImZuYr{yxX$09OWF?ET zJjhDYE*D<9@9&}HyAUH*WUZjqU%8qrfJhU2QA*>z@Io&k`pPgZsfhe{|ACz8?) zVyK1!Q>1dB+5Ou91^2s!!2`ukF9r5Vct%%qD1`#o_kA~GqaHp*L0;fvm$x#ti2`4C z{chE=&{Yb;QGQifo&6>Sk<@*WTTd0YP_VZxb+Q2lvMJ~(3Y9i_emp=ye_+KC+x#0g z3NE;X;9%E=Jrqot61UCivUvlwYXXa=uM5S@oiY6=XNS?VL)GjMJ+iH}J^V#@BZ^k4 G6ySesca^#T literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/AModule.cpp.8C79E6793B0ABDDC.idx b/.cache/clangd/index/AModule.cpp.8C79E6793B0ABDDC.idx new file mode 100644 index 0000000000000000000000000000000000000000..c782cda4988b99322fe60e0a6a72ab6213ae373f GIT binary patch literal 3206 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/AModule.hpp.D8753B04060BBA88.idx b/.cache/clangd/index/AModule.hpp.D8753B04060BBA88.idx new file mode 100644 index 0000000000000000000000000000000000000000..89805e901843f756a5e62f3548a9bbe530f5a76b GIT binary patch literal 1730 zcmYM!YfKbJ00;1y;c@KV?Cjnhx7=~d9dgG*h`|IjB~hp#D5BO<)V5YzP%ux@Xuuana6H^ zxB0m_Ih~;ZWUa~FR90MG7z_Xq005qHcc~EqARz!Y?Wz8J&MTGErS11x|Gv=J|6Tlh zIRjq?pY5>JEGk{woPYCR_{QYP4{Y+dH*QPmVAsUgm%C*9s;b@Lnfd)`Yi_mjk7Dj* zHu##7Ze-MF*Y~=n4Ud}No%-hE#-yPf$C|u3=X+DG)YklIJ+S2Zks1G57V7<<-2AO4 zcqH1~Q@QIZo{zuUzq+O5+>W%E8O!Tl{-MgeabVTf)+B%GW|>d<8`w?eVTs#!`>^owH3EJMy=02PTicInNiuf@92V-Z8fFMV+G0C#W!bm z?J5b%@5o;=){~Gty6k9NFs?ISc7AlV;g^T|UeA2XQsQ{M*H(0`z$hWJ^8fv!#b}$>uRR~er{?8*2+}KnxJ<5i8d~pe&-7LH?Kl|%Le>3s zvVn%yw~TFmHOc$ct`|6>i#Y{ zfrhrer>rmQ!N{*WB^1uqTLg<$>u(noxeP-tA3?kVNUSL~o<^X*amQcO);%C0D_d_B ztbykre&d#9VsQF%9fCGM^r3oz2H;-!fCLUtEYcrM^X?=lLIK2Pwnfs=PI#Q|3u(Vo zg(M`i6C9!=aDxAI?3O9*)e9q#o%JFw>b3R$-j~MuCM!0IK&QTeNLitw_3q*lkNW>+ zk|9q8Gq{kaV3||MQ=g0_gp_0WgxIuwf7f3SlPF!3$WTI_Vq^v(PwmZof9;Iw;*`Y@ z6NlnRWCGQL>~hR&T=vgp#ZpZWCPJbx9TOo#$i~Db*h~eBjq&$uXN5paoI0n#XsQbn zT;DX-U8&T8U1qi+j-BQ2FC>h>0o8 zWUOo2lJeP^Gusg+f-KlDQHWw1QzZ+923FI)HD1S(TtC!s`0&!0H&d-c5kIUPN(EByXze7tlnoj>D!PSe-(YWu5;r!_Ph zwDon}PAs3E^8DE=sn?PY5x<@$Z2oE_;l&*!fkX)O>J!p<}5w4iGP}|EIKmj zj}q(mNjVE@WY64WICa8Fo}suhH)# z!YpQF1X~ERhmi?v{&Q}5%|$;;<}>mzb18DE!)#&V0-Mgm%)!ke#88x)lPJi*0Cxk! l!^qnbKkpg*XDBX8E*1xQ2kJ`@V19l-OX=&(BW;W<3;<04iB|vs literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/backlight.cpp.8F986B9BECE3CA9B.idx b/.cache/clangd/index/backlight.cpp.8F986B9BECE3CA9B.idx new file mode 100644 index 0000000000000000000000000000000000000000..99750d6c12bb86b1c93034b38e3bd765f6f71bb4 GIT binary patch literal 5394 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 b(;zCUN?Z=3rp2Ew6$yl4rJ)g1XHfqKwtCPz literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/backlight.hpp.0EF1448C3968172E.idx b/.cache/clangd/index/backlight.hpp.0EF1448C3968172E.idx new file mode 100644 index 0000000000000000000000000000000000000000..70c3018bca32f25fb5a88850f9d31a0ed8d7205c GIT binary patch literal 2104 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/battery.cpp.C1194A004F38A6C5.idx b/.cache/clangd/index/battery.cpp.C1194A004F38A6C5.idx new file mode 100644 index 0000000000000000000000000000000000000000..72b2fed76eb57fa05079941546c3c5f580a3af54 GIT binary patch literal 5698 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/battery.hpp.A5CF2192CCD687A2.idx b/.cache/clangd/index/battery.hpp.A5CF2192CCD687A2.idx new file mode 100644 index 0000000000000000000000000000000000000000..b182c02d446b2968a4420d5a0d44d8587f8498a4 GIT binary patch literal 1704 zcmYM#eM}Q)90%~{d0N}&j;GfO7LEefLJPf8O3QR+Q$t)t1|n_>OHdgTgsBWnpg09H zbx>b6;|m&JFbe9(5@c~h9b+g4!N{Bunal)4hs-Gwm&k~lvdy{h;B&n{zWF8JJa%uDo@Gz%NAD!NoQr$(_eXURhZ~5Ye@EHvpEuZ#XI36c z9na1hth$@jGJBfy!hxuR!XVcP6Z$0>PzrIfFyRr6}TbHAT-tywSlL&y>E4y}i-6LZHQb_>Ip|X_*z!QHg0ANmc z&*Mmyd{v8J1i+$3)X@OkWDhY+-1fHl#Hjau0fk5ayF{0jhH{3al)EQ#F z*=&|+D1AyKe<<^8VY7gY62NxRZlj^}3o4#``f;PWSwNu@z;4lPrlIu39gFsM?Y+NE zg#sfc_l%9ak+2hzZd&l)n1* zvGO$HX%0q^tv4YPr}SqkgZ};Mp4M(aQ&}JFiJqtQZLN7nh@8}CK+{-n^Vr@{`i{b< zf^oWF4?z~zhoLZ)(*LkMU`hU3c}zf4B!FYZSO*QIA6yO}Hq?AwZ$KhjAL)sltMm^p zI9{J4L5VL+M|aqIw_wt{!zV5n3UkFGo0URgB zIcX^COA9{qMk4?MMz4pS%nO+h36`}oAF(elX?^Ra*C4@h+BlMM;MWvY-J@a^!Df?L zB3RbTK?FNA4xSN*Jf+POv2S{EHeuuJRG<@Y$_e#&mqsR(vsw~=5E_819HzzIeM zS3L&KAgELym%Gjv+_= zGPV#>u1qQ9sm>o8OP7~u&v!wB#Sk%q8HF6R$w)$uQe*-lPvtRikf+d?FUV6@j1%N2 zBc=!PlWIx}igG8Oa&PHM@A`=WcLt~Dq%V+rxd6R^CptmP0dDd=FmVw7sP~Ts{}|vO S1^>wV$BD1ct3%5M0R9Iv?4*+b literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/clara.hpp.37039B002C851AB7.idx b/.cache/clangd/index/clara.hpp.37039B002C851AB7.idx new file mode 100644 index 0000000000000000000000000000000000000000..01026915693de5844b933966ad2224897c16e282 GIT binary patch literal 30604 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≺Fw=5qR{hMu6Ozhjy^0Pg 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/client.cpp.142786E5739C6086.idx b/.cache/clangd/index/client.cpp.142786E5739C6086.idx new file mode 100644 index 0000000000000000000000000000000000000000..37c6e6aab07f00cb8525460edeb8bcda4f102967 GIT binary patch literal 3324 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/client.hpp.B4D0F538B28EFA9A.idx b/.cache/clangd/index/client.hpp.B4D0F538B28EFA9A.idx new file mode 100644 index 0000000000000000000000000000000000000000..9e866fa1f82266d01faebb4b3d3b8691c78a24a9 GIT binary patch literal 1852 zcmYL~eM}Q)9LJyM=~LP}9?pZd2Q60$J^C_Q>_!AL1i`l|Or}VTLyZaoiUBbN1=KCf z%*~gni<^=Va5EWmz97qVT68*G6kWtQGIdT_!X`{*ZqBzkb;`n{&-MQJ=9hl+ySqO5 zd~!2#{Qglo049{=)K!+&6kz~B1OTAACQ$w|1OR9NsB3PSkvC0~Ahu*(>uLQbIC+z< z${DvZ(b%>2Bki}@S0D6M)m~03-_(}oT%Gam!}xn;eU9?F(O>5E7Q5eXK0QxgbZwn| z$>hTI+nO#Vg-0K34?7i;_h8DJ6-%N$_gv|rN?tT zEy2f2ADx+y;XP7%SZvB&Q50O8{#(qw8p8ppcYfm5*v-99{2A8s3l{labEUZkBeu`i zr_SxzcOt)N)`HHvY2V!4vq8Ie&7FUHH;{_p!3yiDDSv4OXqu1mSA5ya^U{x|D*H3!J+7ToxuVgH z9k;g}>v@=^_VK4Hm-Q8vZxc~8>s^w|qW02kRqIq(6;GUl^#Blri z{Kmy!eOx6VjRLSo^4Msoci4MA5Wl}+;|USPuqVhmSy21-<+IlEUo06Ek(KpUoi+3i z4;6g3(o->}M?gpcI7vz}(@^)%{?c=J;6hucfZ`JYHi;%94Yfa~Tx`#ZNN6x2Q2|)7 zD=9S8{!-PeuXLVzFi$|HM1Unxil(9VS2y1Gtz6#G5Qd@@fDN3%L__U=O+Gl3{q^*x z0;%E!%rg(iCRpDQkU;_1D|uxa>i(aD z1!I=Y*>zVyaw5P+(P*Th_JPu}>KOWi&ne9TRNCMnbR&n+U=32sxTym*Vm;Zy-l~v2HD%SbOBH6iBdF=OxS< z$WdEHWuD|O*UvT%KUz{`<_zMgD+DX4N){6Za#RrJ1>~p?OasVK{0q2A$0|$3%{qc5 zP8vb5{F3Y=*eYA?1lwcn2?RSL90r1&PN$b(SEg$M69sZqkdhxf-k!O^2uN_UKH0=H zfjrfNSphj}1Cszk3O{3B!&BA?JpqCvHIZQqcF0r68QYMfOf!@rM-gV^LY@*E^lz)% zpMCwZ24i`QoQbjBXCIBRYnm&cF${UiE<+ab6jeqjk0jV)w~Qp% z#koubyE(UsaS3_KBf|{x6fZ^<_|cWgZ;xwyyNlz)NrX<%X~RGmD*@1kMlLjx(8z~IeQ4B$#{U6D C(7TiX literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/client.hpp.F7B62724EF944420.idx b/.cache/clangd/index/client.hpp.F7B62724EF944420.idx new file mode 100644 index 0000000000000000000000000000000000000000..d21bff2b44e5b6be0b5a967634aae27e649d72f0 GIT binary patch literal 2630 zcmYk;dstKF9S89DJpq0>;msi=F$p9A5|SJUkn0j6Lq#bf^M-iE%UWb~ErogsDt6X( zqh1OYYw>E=<6GZ@`Q!V%$@lra z=OpKx&y$j(sZ%E^5X%2qQEhcaO<5E|NQn>%*Q|cL6eDDnAynJ?LdogMK9A}`@=wkV zEP2wtX&^s&^pm7bRfn(HkJ!40Y}d~(eCOpYmS38py8NkUDoYprc#6At?;na^d~d~1 zJ@eZh+U)yZeWlXg)tFv&YWYC%j=o(b9eh?tv08k64mPx3s_VFRdFA!+wHd?P-plTPS=_zkUo~r1eRca-x~<{% z-gBn5tW`B_TM{cK%-9(>($)0WQwhtaP5N-hcfOZ?b9!n^ZnL8>XTzHPo+lSz+rTxA^E*afe_*k4fiZ}3`~wenLm7hfB-Qe{)Zj|R{d@6mY!hqb~R^aJ80rH zFdNWG``a|!GWyn@#* z^}p7h-1BAkiO?==R9(ndwTw(9?U#mGS+aXiPOq6ywa_dCYo=5V3 zuHxcZ?=(6yq`rGw{KAz>^7>*>20MZhl-381@NT*29M^ZdU*H50f!?9_(vWuOId@@i z_uF>8ic_;A*o?NZyY!O}^ubkSRbhd%hzLT~kduZqf8jBG^Jzl zW8b;Cy5n*cCu4o8KXtM+|Ihq4K3dt=nj>&_5kb%z^w5y{$o45hmD_Yt#o5?=nlUX^ z>aW+E*J%rvjH);t>wSVRMe0Z2cxiC0eMLmgjbpt_aExn5Q%`k+6bFl(8)S#({^HZo~wF+F%Ov2=mmTpuV?ct-VZ-3Fx(Y zV?n5MTIVE#4im`MW+yY8u!5q*EW$iBh|z-;l$^PI!SU(}Rim7M#b8MyV9Bs}30R6O zvk6#>tg{K&lr{|k+ay~70nsOh2&B2w0`q36#j87Vqh$olU{(^a`EBC~IKio8dSRZb z#jwH(iqs=BTDLxv_x?gmz`z?cj4G_4M1_}~eY1XkNk~Q@flts9$aH0nC*b;#YZ5~X z^AxK!u>Gqk)#n9Fz?@{ZFtRXDsbWIO6jUYx8eS7es6mV-te|h1L|8!uV(?&|g44A9 z7e}HA*xmMA0*>j9MFi6MbR7Yw-+R9HcUYU$W8z3iLl4`c+~E_W^gkIR!wz#H)9GqW&HtztxBp3=lb;&>_%fkZyh zba1F{ed)#4Mohp07IMh*ebV9ObA~wrKEE%AfIr~RWl&+B0>xayJoSh%#PO6L0w&&M zWAb1H6^B8Cc?wMLrEhOEHU052CJ=&9EMo>MC@)ME%u`JmBABOmFf*`%TEU3GJf-21 zCwdg(^bPl}sm5$(KwzH2Ff4CBlQQtTZcM<)8Q{eO4&UomWf5E)zYV@vtiQZcrKHFxldPZb9yfSgyv5@ihfKj}Ju A$N&HU literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/clock.cpp.622BF041106573C2.idx b/.cache/clangd/index/clock.cpp.622BF041106573C2.idx new file mode 100644 index 0000000000000000000000000000000000000000..f3d91abde97b6acdf0c9b6eff3dc3fde49e659cb GIT binary patch literal 4824 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

C?1ntOB?_mA)VX1=q(-F@b> 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/common.cpp.FA1CA1F6B4D16C67.idx b/.cache/clangd/index/common.cpp.FA1CA1F6B4D16C67.idx new file mode 100644 index 0000000000000000000000000000000000000000..f4bdf8fc398755fd1fb842b4c60cf088e201eae7 GIT binary patch literal 1866 zcmX|=dr(v77RApw`3NC=NjT;t0f7Vvd5{1xA<9GENCX5?6!Hlw4+WJ<5u*2k6~WR6 z;1!Tp1!>iaBKYD`Kq!JJSdpm#Yqhp;v4|G%)jN1a9ns#2op%4RX4dR~_WrF|G2uF0 zusMc>Y!1)KNZpwXFbrd17-ra+y^Tr3uu*di%c;+cP0nmyL_KVDebf3fCE|9^g;eR@ zRhJL=0-Dd%n;)oCzaY8GuZQ2fIDZs2erZuLW_b40ug5QxRS-5}PdfZY&YVwhV_B;T z_1ps|4Vx2;^JnS*nixBB$at~z$d>$aKU622O^h5pm;BMMz6r*=q4xP{u;xh1;`+z~ z2QzAoPk`Y`Ws=W@yywny_Wjw>_q&pN?@SETcaG1V4j$gUxX0(ef~e=8Zn$!8lj6ljjVB&1URB!Qm(T#Tt`}Z#YF6PV3u9bguoDG<{6m)@hr|h!0_)6np(Z0U$ z&aSTgSvB0Om*1ROzNv}XHTmyM$jgLN}kka7mXjaYmkU~=Iu9@OC6O?PK6Oh z^(S8D>mq{mnWt;r`(E=EGj7xJ39p)ges8kH?SS#J)tbxIN$rDIA308jRxx{zljP6d zwq~Ixo8v8lXY(Sy{Q|JIE*qZM)n?|B$0?~!cdw|v|BMG79u1rBZY`==`smRmcZQCZ zx^Li8Q+c6&S3qA&OgXdeK{Nd9<*aUt?Uh3x$Nm06;Z=p)-`|+k)KZ@lSwXBF-I`eP zwxTlg_e;TmNc*-g;}7tJmvJ!_{PUzx%+Tp)8B=naa`qlze*E z?wFXc$+_o`V=H9-aW~r&V#gYvjG)<4Iphvp-OD~p?^(|lxSE;f2|a<{eR z=ZKPyH7?xoaG(2*nO?J~e;m5WXqeFHETa2LOD^`oW!{2?jKwWObK#};@aVf6X|n}u z>V{6-+9PJvByxi^8;i;dic0o+De!48zu}eVl1W z7HF)9bqN?^3YpG&wmF-HF-+CLFl_I@k&e=@qc8J`xC9IlHiSqI3CQ?+pPia!2%}*b zEp52%m0L4OK%@a0Hbh#WWt$mp=1scaSZxjvOU!Zxh~$$3fG9p?Qdi0iAS##&0f@)v z2>{}$c^b=Ot2+F`K420MNtsd-BAHMoVV@}6F5S38NrT8nWut+}*2mTlB0G)U;srVB z`k~cEe;Pz0TahC~a*13CkxHUca=8t$E^qZ|I7AwbhAXr=c-Fhr*#d{i44BcC`r7!T zC+ydotht$`x-X-{dVI@k21JfBM+HPqGA9K@VwqS0k+aNM0g*zj&_bkCE42{0s$I1Z zxvAZ>UXKoXK7OI8#UZjJEZHl5;T=EoS=Y-)S!j2k*Z))ued3`|AbeG=M~Wkqtm{2e}v^m4iy$sjJR8 z7dUm72@s7(vj>PKrpWG0Iy2e)vDgg^*KVw0k`vs#4& zM1TMm5YYkM@|Gd)yihYEq(MZGgaE#$0TGkWw7b1z-P!i^Src1Nrl-}|#D^O0wEtBT zTL@VHxqI(bN^}1m93lxr!XKZC7HzcQb}}Gh*|NkCap)X2L?oN!LqxGDlR8tb5K%r< zFho2yj}H;gg{OWJL?q6QqErGRIY%ynNX1c!vQxJwn|f!+-eeG97>1+xqUlqKK4b52 zJ*NM&lcw2}v3a}6u8obwm)k6~)h!KO6ru=J25B8!gWbH;k|k0{Pam0!SZMF;Z|CIa yy-eimE)Vd~ELK@Z^TQ)n#Dww8xP&E(4b2%eCLLH%oK=DaR^-a~$Td-`vHt_G*^UPQ literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/cpu.hpp.A6F11840DCF8A513.idx b/.cache/clangd/index/cpu.hpp.A6F11840DCF8A513.idx new file mode 100644 index 0000000000000000000000000000000000000000..3528a582327d9f675f562e78f75385668ba4f881 GIT binary patch literal 1026 zcmYL{e@IhN6vywo*X`{+@7&%t_ugKsrBcc)BU6ji!k<}$nf3#x2;~e5&Dx|CMJzM= zVMZyE`J+O!Oc`kwh8Yryr4mES@;U)8Q&N(UaMc&bF18jCCzlS8ZE%`vm0X9itnV zp0k$B#K#k>(rp1#^;NAib%ikl*HtNPvn$8^e>Qy|u54XYnv`pp?s^w%%x-@XTpiM$ zIThti-nuiF@w?M>FQ_%-==q+qU+uGhlXp)$uT1Fvz{c{yP-}7iUVj3Bu!kFUs`z&{ zEhIER2*f}}0%R^A008sP+0hTq5W5n}MPF;whPnMyRV}CQN}3CNpi%=6Z{)*;OtmDoW| zs^#tu_CvCOldHN9D4~z&vqsi4!JE=KXn6GGr4M8@0O_^*#X@q||LC6|>ASJZ%tBuc zKq{z0LUQ|-oPE|v7pdq$+EHxMcwxlpI0HtU9~X!buj36E@qT=uh?1ZLgt2oGr5|c0 zVjxCZsHHJt<*aWSmC&D`qpXG)F$}|E#Ohi99m%S|sS8mOh!IC}a*TMAmz&3eSBz)& z$_b40GQF?32Z9p#i`WTTz+KZ^vgXXdKqY|@vz!SQp%atkmar)cFYJ?F>vKS*y z&2bp5eA(eEb!;*2B>>DO9Qm7R=aSa3|;1{VAlt5dAOwa;cktqQe zSXy%O_PDOeYRR@%xC9oCONjr6<_hV;iz~c6lJrQ*BWaJ6c_eb!AjjFd!f9_Tk&-fs MhF(D8@u5fl1E}f+LI3~& literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/custom.cpp.EA9CE9AF81C1E5D4.idx b/.cache/clangd/index/custom.cpp.EA9CE9AF81C1E5D4.idx new file mode 100644 index 0000000000000000000000000000000000000000..17765069753c33553cfbe832fa938bd6e49f04f0 GIT binary patch literal 4732 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/disk.cpp.1F7580C201D672A2.idx b/.cache/clangd/index/disk.cpp.1F7580C201D672A2.idx new file mode 100644 index 0000000000000000000000000000000000000000..b5c499e9b9b886a69e4abecc68594fe3720035ca GIT binary patch literal 1998 zcmZuxc~DdL7QQ#!ge3efBw-B@g3$;BLlS~WA|wPt1cHIcl4GzgK(NIPf#*O`Tyc9m zs#Tw$XssZ-MQMu)Vnsl$SX>aFRY2(zaUV-9F6j6syy^Svoj=Z;nKNgZ@B7Y~7o*c% zw8gN@Dm(TJe7GMyTvr>W25(g5=_%3=aH}opEjR@L&^jT&&REucdv_QP}UESmxQT@&|=GcU+dt z_uO};K1)pZv*$iJ#c(&qNruUxjV0%FBj%Z|KgX^<#oUyWQ2jc2zP5q)XWLI^qH=e0 z_-~7nE_XDftlu_l)J*@}TpQ@7yzFsk6(gN#Zf_EEp4F}`Z0kC9A?r6rj6Qon*CZZK zG@NYvDDWA!q&9UWu4xGWKCklBhwdS>?n*-Iuqe`VM`fou?co_8U+R0|;l8;8PlDoO znTocLV_zm`bY2p*u4&jg@v7|W#`dad@kQ@M@;7v*IeHa`k*AU z`EM!9shY86g6Lj7+i^pk+hN;lRp#2BUsG!Kx#s(JKY4TD1M`t!>)5BpjA`4-9<{Bk zu(%J5Z3*}4Ti|^mRN7!1P>5A^lSa*~9yDDzV>V0*F!CI?#w@_+dpOJ;7r4TjldUi;y#xSmZx zAR_aol8j^_(2`mff+#YIg+NE@SP15jb65zX$!OMJaN*2Jtn_!CD;u|_{DLD8IfxwN zuCwmPM8t2$5y-veK8914k$pkW{V52LGvXmo2~?taYtOAz-|FIQ2Xn0hLBYY5J}E=?TRnHV6nd;fy9nBVfC*-ScJ$s85r> z&Y~b7J;U+jxI01Af-LA+L>8bk{&$a?ik<$@q2o1C7rk_ja*rqKu z2m+)5AqXUq5<}! z*4Bw$e0h|!i$Z|hN$yI3##6)pF7~LZCry(|ML?s`>=2Ly$w0t&;&Txwxk@ht(cEaS z)4F~6--VCfu_b`&O!XjuI)yr&m@FY6Kp7B5fMl*DfdJ`jsfvKGDPap*{~cbpuJfxF zE6WVIWBaGjz~{09zvBq_?tK20hMd&Zwr!Uv|K$#WfnuOv6_#EbJ@~K>M-WGeWA-|+ z2X7P|^}`Wx7#!A(wUIYpgpAhNA_(9I_#lwTBnkvFnM{Eo$UevgL9i$|5J7}}gbM<# zNE_H6JpcHQSz}o^0t3^)>W^O&^*iyb!MX$DI}Qx540L>~>b431(aFI(_ur<~cizSk zgwjHt?!QF!tEy$?|A(OG8p@*adtkv4XzVl|SdL}+66+Vp%}LL7wFaiir(iW4Jo@yp zjP!`RxCu*Nk+IyGN=i<~=WyLUbhD$Rk)m*aO_2AD2yuu^7(9K-ETxa%R4?8%m8T#~ xq2-4L_^RcBGbL{EE-|tCdC|^PM~WS3k8J5SOd7#(a!7FHu-FE3T;g0K_8+q*x$FP{ literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/disk.hpp.E57994872D8447F8.idx b/.cache/clangd/index/disk.hpp.E57994872D8447F8.idx new file mode 100644 index 0000000000000000000000000000000000000000..4ebdd74e448bb6a72fe77db95e33b9a2a370144e GIT binary patch literal 728 zcmWIYbaT7F#K7R3;#rZKT9U}Zz`(%Ez`#&kQk1!qfq~%^BLhRloT(H2xepoe9Q~cT zN3Zz!U6KBKwtY92t+}!5(vmK5``uspgA*UL$TS4MtH>AWb07wrxP8OJ#j z+o#*A6}Z3MtQe77=)dn(@*L;(gDL(z%XXZhHnyy6V@ioAsDa@5)*wrQ^MkfAR`mBewZ0KMoyeD%up&alKkJ@Ry%$a=Dg!v(u|T z9XGc<{{{DLLfz8-#(8t8X%qzqz!NV-fF3bz_95cjIJYbbP%zVsz>?s$t zbuPy2;AP}t7UB>RojkAFxa+`yRz@CXUUgm_n4cLr!G48#k&z4RwNtB$q6^t~A7|uY z<`&_Wf_Z?E3GDEq)SN_+KZ}c!i-SQ7=H-8E>ta^Pm9cTKb272AFt9>`0R%V^OlX9G N06RjI1HoiMFaZ~|x9$J{ literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/factory.cpp.CEDE1C1627112064.idx b/.cache/clangd/index/factory.cpp.CEDE1C1627112064.idx new file mode 100644 index 0000000000000000000000000000000000000000..554a2f5a49579a1559668c1acc204b5b9722a024 GIT binary patch literal 2394 zcmX|>X;jl^7RLW?c$Gk2LjDF4!V(}61B4}zKmq|0mQW2#6cV|B+7ekrV8p})TaP@4;I8x6dl?*IHz;J-E+S@=ef`CNt=+Em}W;& z@wpSqO7iC9V2Yv~D2lSqu@nw5D5~C;qRMtGHs;JaBy;Mn5BT`~JM+v-EvNFvEt+`H zt(-Z!wC!Dm(}6DV^?S7a(nCcb*}k{+QVUs;bMVP zRl&d88+QyP*=*Uw@ z_Eo#AEz?ge|K-8pBhb4R9LBg#Rl`g$A`^(8anYXSMF_z5ET9ngyy!YOZrS4sQ zWi!NkB=nN}uDHZ);aR^s~NLJGH=^wR5&Zj){4- ztWq#GZ&Bcj=CyI1jw{YRyV{-?aCc}^uS;i3U;H~|Pwy9tJ%6np`(@Jy+HS|C?T5EJ z@qK8eruRds$W+mn`(%%od*-rP4~7rrI9~H!aB;!9P5+tO+du61Q$W5cF1K`@m3Rlm zew*IjKX@nE^O1$M@eQv>uS)TmJwG*}IH}qyyKF4=DVt%r9i4whZ@DmE;k{c`P<_DP zXp+W`Z@x9*&-Hb5N>9X@)JCuRhDj&WZtA{IdvJ7lox4%6*u=ZN;IrYr2G;W1<1@Vm zg8hGcWIBBA;;T9@zCvmekpAv(g2MkEGV18V&e30+j4drSPt$Uu2R7t7tBUu`_Zv)4 zZEhJBNxJXuo3^^(<>Y10e#tubiofu*EUoGvVHtuaT6gz(-ibRGu94T(J{>jnN=@p= zy?fVH-@nz6k{`=3{1z@vrQ7wAIL(8*%@`i8Y}idsV~JsvExWHaE3?_wro-?} z91M29g#_Z2U$4-+m^S!y?aEG&`xc$Nws3Xh?n>fiG%Y^kccstmm*MSitloYP*ZUd# z6HnyB+|+y5U%VEFxwyY2qq~+=eKltsWi2l;YblD-IPb1L6>w!0hmo000T$ta3^hyb zOfg4Lp(v^@t}WU5?6%iD#^h`Y7&OBrBa#tm_y4;kFW*`)ilV3^N0(0OFO?z!IUzY= zcB{m*^qX$>BLWhUB$%^8S={=siYbVIPNb94OVpKZ+q7y#ATKU2aZK6FjO&xyVi*MS zobp@=%y63F${PrXbLLgdMg$BZgO8x9xO01aUne4<7wH4s;_0q~*>VFS!13h-xX+(@ zpy|%MX9xq2=J7q*%OX}YZ#=dkknfo9PN2}S(49b$Ly@cJYjl%4`*1iS!1Lt=Db8vH zV-lVmLl|VVjIUmnvg(iW4hg~_q=o#bw)S3c8{cb)087O3`^zh2|H&v#;Ql+@;EYTg z0t_a@k$iH802W}c#F~9mcI`Mch%hK=C4c;#!>%RoJF;ys(|83<0Jm)5n~&ax-&cMK>a# z6e-7o9w1|6;An_JIb&guL^j0gxN!k|LW+O@a0@wPb=U`%2F0ag$zi~uKu zqawhK<|Yu}Mewu)1WG{+0bzttOF*m=YY0dpC9wpgfl>tlnZGQAfFeL4C!mxnLkXzE z)zJjBLE10^I+;#MKp&!45is~0LI@ZGjB)}H3K0a%QReurkxq_hH1kw=#)AY2E9rjfx!@B2*qHO8xd^igx7E<7zl7>a_5f-hxNOxu?D^Fc&FCX)HwOer~4GqCCj!l0(r{0{5n z24Uz6AtE3ZNqz6cU;I2u&wh^xaDBOq4LEu@7+Nd6tsf>`%sVN)j;c1L_q8( z4(z|KvVRPx# literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/factory.hpp.2AEC37A00908E370.idx b/.cache/clangd/index/factory.hpp.2AEC37A00908E370.idx new file mode 100644 index 0000000000000000000000000000000000000000..330bbbae66fa651d2529daf66b2f3090d4257bb9 GIT binary patch literal 904 zcmWIYbaQK9W?*nm@vO*AElFfyU|`^7U|=XNDa!oLz`&r$%D_-DXYLfoydw%cu9g=! zoM+tide`X_%zls93=RcMxy?U0Z0(h+7TgmY|9|Y&y}74xbqrr|yOH$q%!Q?9+^lof zY?XH3^2USf2ERqyv7n-bm0Ohe$;V{W5&D)Z?Rv8lC;+> zQdN!JFxh3BSitmPD;YC~DCsp7oO12i3Co}CjCXd|R&91JEz~~N!>wVnreIFjy}79ZeT+P;){55ZFsCtcft^&!-v7oa z_c|XF537}cl`s#h9fut^%z2DlU}sI9S8d#N;6N)#jhdA<534<&y)ev)jGSOw9!B1l z_<7IZKgbp)Yfgru)SN_6SQQr~7Yj2oFffQg!VUyD5KKt~lN-U5KrmTw(|L3U0aUT$H2HUT~%7A^?}K7?9m^nidkLR1976h$yW=^YA$VGITab_7!Z c!4yI;xe!bq1XB>fltM5$5ll7&lOMqZ02w^cI{*Lx literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/format.hpp.A472BFB350A64B30.idx b/.cache/clangd/index/format.hpp.A472BFB350A64B30.idx new file mode 100644 index 0000000000000000000000000000000000000000..c466d85fd1d2e778f6ae4d6593714c58bc24e7dd GIT binary patch literal 964 zcmYMyS!fec6vpv0_l%Ry&FN%mlZLuCjkH7)TR{^{ja6HWoy4UsC@Quwm1o|@71Gf>^p zboKXvuUTF3K({^J9=clC_f7o*+ifg3f1*_JM$~XRcDQIyPoi$I3NyFPTf-(4uX9{P3VH_zTMaks1Knav-nI5TDB4Q0hAMQb~`&fjur zGEc2+tQ{u-%zH5R(Pt%IsIhD_Ah&Xd2xRqsJ^(C^)OYOra%io_NCC8)?G};DzVXQS z+CxvLb!yC_0Os%NnH~`9uY!w0Z@Bd3Ey}EmJ?}*K#D-@R1M?#y~mMfAuA(^O4?U4ZN z&5LKI$%H1G$;kx|VX%L&fH3Ng78ydwg$!Xn}Ad+ z%?%He-|CJ_gh6+(*sx+;7!i8NV>5sxE?^7~Bs3`|&V>e{-{UXdnYVvYi$5J>GU_FI zsf_v zMHnlI1upxl50t*#GFFns{~CT07j6cZ%movP^9gMZ+dx8ZzPHfOVq7TEU#n!Ip{DmX br;_nhUhfN@-Vz`1wtZx&B*VX(-uwIkZn)q# literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/host.cpp.0C1CF26FC798A71A.idx b/.cache/clangd/index/host.cpp.0C1CF26FC798A71A.idx new file mode 100644 index 0000000000000000000000000000000000000000..f29f9b635674a94d8aff6d76ac7ae5b91a940abe GIT binary patch literal 4080 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/host.hpp.D6E6FF9FFAF571DF.idx b/.cache/clangd/index/host.hpp.D6E6FF9FFAF571DF.idx new file mode 100644 index 0000000000000000000000000000000000000000..3d38ae58573fb8663b45953337bb66f04bcd3460 GIT binary patch literal 1978 zcmYL~c}x^n9LL|hU08m*Z-#Y-J%&9wc2~3r=z8NKMX8`7a@4M<5fP*jRE`K#W39zD zQl${7rd6q0H8xr`Rwcv(0jbnlsRvrC*wSbaMa2`VCT)0jhWE!eFZty6GMV@N%%mj5 z#~)T96t^&;G(WpIQ;85_5kf`9g&)Q;2z`zbD&6{3$}dTZD004SSD6Iz%99u7duLrrIjP@O z+SHVw`MW$>vu9W8{PjH~y{2RWx8`~7_B&g@yoLMJsWopVKO8TX&H63Zy0dLwS@Xzs zzou;Zn(G?z!mr@iM(+mgmd6o6#peRpn;*?3p|xf{{7w$(Z0M`>XM#ap{A+i#J#9u(icxi9MA^)Me$K+ohkJntrQ&!#&0A^n{WtuXbPV z>(HmIsw!J}`RR-c?Y%#@Je|bnJ;@VsYuIR?wo93TVZG^7mAr6%eC)>LHovP4)dv?| zd%a4L^62`^V@c;52R~-_toyy)Q}-3gyn*wP;gWxB3#R8*F~pa~@EN1gno&Y$3_dH3-p}ci2V9rIYP)U{bJw#oy+wc_O&1gafeK#A#-hYao_64O-FgG zmFA6{(Jb?kbvG&x6uya3VaAL=N@_S&Ste+oFPvLJA{9ph=pyd`+$qL;a*8V>6%2%K)` zcp5T4|6^cObNjho4jU{8TyB?#hRl01_TKz+Q%n+v11$(b-J#JmWL}t^Qxv2aXfgw| z!Ky9qMR8qCyn+DlNKOKUG{r?$6KGATcRYuMzfO$a6A`Cz{xsw z1YE33Cv}_w+U>#P8okHuS#}=+>}+-(0WOkr6R=vX!33N`oe>0FLtPP4^A&F!Pw1@> z@K^Y&#w|LVIPz-eHcY@6VRRD^qr_2?2Mo~uOSNazwC+;N8KCWsKe6^;8irwF!3fM0h7(-AYe)~B@-}v%##S%Sern=&f0~vB;Iwm zI@*UJU{zUJ$rT2uBa#{nP$e=hLP2MF;0FoFL;9GisHRfPT1s z907yd5KX{1+89GX94p33iZBdSV?)fa28DO-aZErj>g@y!CPVPX+7%-zPM=ckYb8U#q%sl73r0(1ObD?5OpWg=b6gi@I`;kX0ToY zoP&!XU=^$u0#3Vg2mzPfHAK>bQBy7cJ@o3?vzDrv{k0#%ze_+EE5s4djn&1W!tA`v z{%?9wVOCLU|3*ze@pSnOY{|eDESme@MP^;zBIJ^c4XGeWH|CYsIH69VsrYqEwNhh>GG=MG$=}yru8^b=KPJd}pt7_TCB6Q>Uh| zFf4pQ^ve9~l1viAFb;-c#U(|HV`vyw&&05m2ev2o7S_ht48&i5k^QPpTGxHkZ^>Wl zMR`9(>JBZxX{&FaB#csAxP3KvsJD!FEXia3E7#nj?}p#J8Thr{X*aX=OCtaIr`)U8 zuG#6o*!X)&!2Ztgj*sqzK>O-_R>EHne*Ln4sJBZxSsQ|CG#!gGa|VNNJnm+1&sfOa zTXHg=xNF9)Kbx&G%eHzR zuzNWtM_O*`u;(}|SxTFx^K}^ZlC7$2cw}BUw|{fFTT9odSIg=ZgR)Dv7Z1v(>^=Wu zaYx-FUbC(_=95*9!t#b~$Jf?ar3}*hhK{_}>Do6wCVGmkQvF9)jN3nuE?OhMdD;2x z+xxN3)5J%<2ri6vZy0$K?;tNfe6-8Hg0@5)+})C~^wsH&YuFJlHl5z#xBA8C*gdNj z%|Np0{GuC%efy>*iP7IL?0qkXM--gcbxRS>D*QV1&x1ni-V=9Z+gQSX3-WfAoa_iX zzRPqcaL>$HH7(IeRgLvkhmsz%Z#)!evRHweuKmOi4Y@R?sOPTTFi;>Of0Qj&p7sddf?Wi9eD?jiff(ra~}H8?tSMX z)ptJK)zts+;pNJ8Q_FkG%FgCKTTqc)a;8cBIKN}}f-|u(&rZbeZR08LTKUG?BwmVI z9%pVlqOW*fdZ}#Znut&5E&6diZSIcJF|nq{x>JyJ(cPR@bJ#(6QYmxM+uD8RdrJMP z+Szl`?<4cVm$uVA6w93w!&u+CuG_Q9CF?VRD>%=NE5lO z@7=M#PF>XW`7B7I3b8Af`)j+F?k{xjoS#Civz>7{pivTk%5~L6J!` zO;3t-q918leRd!zaAdLWz_FMnwLsbM(iQ(F#haYzUsWRy8XI4qcdlfkYv3G9v=fKH!79vxdT9MVgpM$-scAU~0{5g3bGY z7iBLjUT@*rTAi7Lg&ijluo-L$f!0rJq*<@n@npeQqc{S&TCSrl{QcC3w$YkEz$Lf{ z0gvDz)+SY9%%0zd5dylFZbZP)GK>hA)=Wn#&<^`6bcmxB-Ro~1QI97WCU`d zT!Fw%?509s5}Q;AqCBI5_&tf6+Whuc;0Ux5txT{oH}&UPoxwN)1KVINzTs8sYqPo! zCxOr4Q;yP|O@?x37LGtfh)4w`s%rhx&kjcr&I;$bZ)mP9-nh1uAi+4v=uJY9EXd6x zwHgk6Ac$WTP3%0UK0om3WExPA8d{^rp}$adlUQZB-pO0xpMZ8>U)+BD7-ot%dLmLsHluWW*EH za+XGd+EJ|_LFcGbkYJP<9b;GMbL{Wm^rs=4>hZ<7_vMfRFgXGFuc&-tJ2H_0<}V|L7-FUH2ceX)R%5cHlbEKU#b;(9Ew&A68oE1!_$#&&wA5e2`U#BhW}R z5p7eeRyK#;>!y)Fi6|EmC=be)oVdj|{hyJQ2m)9E!f)@6KCP_$egH>6r_)(K zA1#`_%<4=B?SD5#5JH475yTO3%s>3nnoj3F%f%6dP$B%A-Mu4OjTf%s2m%>_Ht)j$ zCnl1ikNe5ajvvQACrD6h)kYF@T3tY2T94%QE!{iIECF3QfL1%Raw`wu2#k(KxA*aZ zQxjeu3M0~A_L#GA1VQW|`=@)yrpw=*Ol2WJ3?xDzWk^K`?38w12<%CFAp#Gkhb;mP zLnA`q#q_d8pm)}LBk*Q;i^fk5Xf_QVId55)LhFMSW#?sDGPAg7ezB*eNS%mK%fsQ= zuimGZZ`i(>W=>7R%pU+O?f>Qc`~`WImZaolJW6V3KQ%B?7p@NV2=h^BOxgf17e7}g zf1{hb%F)3&Sf-p}h>!(*na`87%(^h literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/idle_inhibitor.hpp.A4AFAA5C5DDDE471.idx b/.cache/clangd/index/idle_inhibitor.hpp.A4AFAA5C5DDDE471.idx new file mode 100644 index 0000000000000000000000000000000000000000..632db4796066a727ccd52fb0ef17a1933b1754ad GIT binary patch literal 898 zcmWIYbaSg>W?*nm@vO*AElFfyU|`^7U|=XNDau^Tz`#(#$iPrBr+30`uEPdAu3ufR zzFV8TAbH{9z$8_%AFc9J^!|REF)7GrdXAITcdu{*$>YHrjJ$g#(@sf+6|NDu=yqO0 zTyy3J$Mts3oZZ=NAFCG6ynA$6N!z=xktKzP7S3B*YjU>G@ulLHnqZz+VRb)VX|FS^ ze#Lzt=*6n)J7&iXq%tge6gw{lXk86kpz1vJ;ZIA)mCS*!_8b4%!uuuI6$6aU|`^ESI+mEcJ@6x6C)o38z&p5 zFqmNAgvc|pF$Qjs$x(B=e?fqWgCC-YO^97c7;ZvxeCXy(mR&PNnRsCG{HpxgaQXB+ z<*iRE!cL1aalz#IRQYt^^0Vr0Yh8RG*}=!e&CkFlrXZ#QCgAe(=iYy?{@?#;0!+Lx z{k-hFym0xuFQ)&>I2TkU&LjYn=V9VuhReU0lJv$osytGRi4i8x4YCRr0w1UU_L2X; zDV&dqlb?Z2R6$f9Ou*%fQqzhRkV2A?3mk&5fMezY2N)07P#!iOW*z~~HSc#Msaigsd`vuS+(z7{JZvHyBHWYbRU3C5IM50*!G+rs7M_e;;BbTmA0sC? z*!DdA^>Om#qt6(5*!cMPgift4iY{c|eVmbpjZ>CW85Wj|jNmYQ7l4=;C4lTLP6s zGX(?*7)fkEENer;DIh3hrp9bRDiEW{DBi*x_QD@jc0QjcF)k)% z$N^ZtJFXx%D?h^kz!iW!KW|Sg1GaIXpdl-9bNN7^bv|LT{o1MOcOnvQ)f_mIk|WOE zd+yPnYMRq3XRj1r?0jO~TtML9g^Y=cva*Fw7F?8WTCio1cQKI;0H1-m4LO@>UiWgmWb@$|OttmwDTU9Fls=Q=jMMU1|boZ7hI zubh)_e%rERz42&kVteJ-%l6u-@}z@@x@)F>`!xM#!~T}A_QsG-$K@SfxpOm~1x2M3 zUzZ&Eu$dSAu@9+73}2>g>@R=3qhl<;YZ((4(K|QL89#fprg-^6+Reh=b@syC%y|wt z#?>ZOSJ$UYxB$wHyLl5}G5|il-b^u5=Ex&GLzyiS98iwj(UqWEn61{B;&6uu2_v8a zcz~He3r1&HYQy7u>R(tR;Rz@+H%o%<43Fb$hOhrQ{*;6xpcGf`1l<`AegAQE8E&^p zcmT?kyIKgkGlH}0>V2MmxL?8xP)2T)1l{7X{n4{M*2q{11E6f&=0VWCCH#KLY;8l@ zTN0}P<;VSO1l=O~R!_7g?XwsOCqVgge=9+^h)=eZ2iT(GBm|(mxwj`lw@C8)V)}Y= zsuUUf846!O%l$4a^Wskf^KnQj6{ zI=;Fhaz?@pP#)aFOrV7yd1+djKAq~nu|~oIC@Z&0f^N~Zw*g~|-GvfnK$*D7LePCj z&(?_O*Z}7zB^&_d!+p#I-FIBe+4}eSt3hFMsqWLVS61nJZqGC}S%VN8&yUjLTt=TAH{&V=Gd?ykvVf+T5X qm>?sX1}5nGwL53x!CmKNy_T^e%$~Q)?!5A#UN1H@cFx~F2>b^!D}z%2 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/item.cpp.BDF1AC58410539C1.idx b/.cache/clangd/index/item.cpp.BDF1AC58410539C1.idx new file mode 100644 index 0000000000000000000000000000000000000000..cc3ededd68a1ae68fd692b777a7332dfadf756e5 GIT binary patch literal 7280 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/language.hpp.045E99AD59170347.idx b/.cache/clangd/index/language.hpp.045E99AD59170347.idx new file mode 100644 index 0000000000000000000000000000000000000000..e5629625db90e05ef7f02f0be509e690ffbabb2d GIT binary patch literal 954 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 Y=oF?F!QgY33bn~ezCnxqmn;MS0wE?=lK=n! literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/main.cpp.9D6EE073CE3F67E9.idx b/.cache/clangd/index/main.cpp.9D6EE073CE3F67E9.idx new file mode 100644 index 0000000000000000000000000000000000000000..986d6c37ddc99c10c7655b0e407765630e8d5417 GIT binary patch literal 2136 zcmYL}cTm&W7RT=|MH6yE8W@s52q0CY1PF#2l#)b<0mMrUH3%pRf}&WSB6d^|WcR6v zyF3Yki;5r}1uO1zD0W3fMP!wgeOOpQ7k9#&k^9GYJ~Q8$JLjD5y(dKG@2?~Qup~*g zAzi&To&W%#0syktW~GYo0PH0Ju%Yb0^7xEnZhFI2fu-dWJ9IXB?v_ z`4Ky*Q}(L?k8{WT?@zsb=libKS}-a$^>j>(x3GEO)pn0~Os9OPe`4w}n7Hz}U#2Ng zzigR)zjE%w61DUCuWI(-4R2;fd(P$g8#6u5=6BR>{d1~bP*?GgfA(AFSklFFRpq0f z*U&z6sLq9NJanhPY%nKZ)!uOYhFzKa2SN0uFoV`Y#Y1BM+lI;CRz2_h>;6sW@$GLO zX8YrJ`ceicV{AU5IPzzV? z2LDamyZa}COs430k$!Qmx%lKK|B-@((qQE^S}@>*(IgWv03knMZj3bvzyi% zwp61R`=6!s6{pu0Ojh)=^Ou`84!7o|?9I7sclFdo_8x51@NBoJ?c&&aIawU^Wpd!I&E8HL^R|fF#b-Z!b(tyd{t(0ifGBe)%ozbd`^^9VdpJvf zcxI^&s2~;qL}rlnXJppzwZ0&=D(!+m1px9R_>pGhyI=TsNcXPnq;?1r06=y`yLmJ6 z-5+eK-ko;moF5g$0f0yp5^F}j`&sJb>^wXGxZ_Uu2#?}QFo?Kxu0HOJGBE93=`9Q* zBfgQDP8iM0=GX!<29biL;8Dao-skb|F2q4Z!_g=Z32{OSeIRsqv*>6c9-;u#01Jp@ zma+v9DXo_Fm8Zv`!47U|KMbEt+IL$-S0?K_oB`81a@k z@R*s>IT%Dt9VUY}b-t}2!h1aiQ4BjqU{svoyT0{bLJT4jk)*>fDr)~N+Lne9kdfHP z-6WwfI<6+X7Y~u1jh+x9v7a~yB8jgg&~nvjdZLGY5(bf!E@dxxm#lI8{03<|vQjuW zfBtFMgRAQD3{10o5OFd&M8Q4EMwP{r`e4ei=Y zzn_9ZWX3Y%_>cS{jx4Txq#c^j$VFy+W7pP^7h^bxjOQ8iAhM)e>W7m3I?~njgBV0A zYnA=-!)I!}S~IFJh!7j`R`l#SVQ&<51|!U*q^hrLuluBq#0W^h7YO36;MPduO*I%q zd^(@4wkpcJy<}xCMwqEN?TP8_$rF{?7-6QPRd_-9U~^k2o`CoUd=3Heg?uLhG8P-V zrMKkjhV97-!yqzb8L~4pH7_Da(JL^BJaL|s?OdlC!+`p1ZI0Xk0YoxmnK?vC6Q$+8 zLQ!LIxAO=F5m|>!EATD|h~<90f)QrkY>|K3}5_6DIJwAC&>|wjr+LDN5a@Toaec&hK6L^Sd zEE*Rg4u!*nNXQa$d-0pe%*;k797IyGlm^i<$}+~l&fy0ahluBK5V;ZDND%oDd`Qnt z11){8@)lzRq;gaVUwUNb@dsEpFo;}9uI$(2EzdWm=h$geu;4Eo?Y`vk-nZ2cgNVoE zu_vFlSAD39PsKq*#Zh%2a=5P v4?8PwC!veI!LoTW`O*-nK9Pl^P<7#45?*HxfviVUvYB*-A~7&DC>Z<$#?bcP literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/memory.hpp.220BFCF008454788.idx b/.cache/clangd/index/memory.hpp.220BFCF008454788.idx new file mode 100644 index 0000000000000000000000000000000000000000..a16e087593e14b38a14b55d1c49be96500af30d7 GIT binary patch literal 740 zcmWIYbaT7I#K7R3;#rZKT9U}Zz`(%Ez`#&kQk1!cfq|ixk%6INPVa<`e1{cyTz<}6 zXYuyLwR30h7#1}NdI(Hl42}Ht*41;#s;qX8xSai)Yt*Z_CWIz@5K_x@E`g>swJy^c6<^jWcgiD~_fmnA#5HWjPu zZ(^S8efY58mU9zL4PPog_9jGSOEO`cb6+;!kU zDc~nzL7RKKTFUd=&Y!I^Xl8^+15CRew0|+7_iXai~kdjT5rCShf1Qk%A)j>8z zVcdw=?E(s@XgiJzV-y>-m1&SxQCtv(VNjtNZBQ8$ofGD0{Z&=B&Z+PI?sxCv(2x*0 zjv#ZALbKD8vzB8Bf-n#S$;?{2Qfz@B2#z4x)p`23HGNucyH5BvdA;7pHEVrCNvnm! zu^ry_PMbBuWdp4gmvF*|wq#=HLCVd*l9&=!Pv(dz;7ULQfz4X?J|IyHY$Dkh`*lW^BFnr|B2_?Af8VHD#lHWu46#T-P^v z(VOoCN3IsGSoZ9U-kzUTw%#rx(7*~SFV&u<1!hgZmZ zH~qIsp>MzU;OatuNt#|O*}25CW#=^)@utry&#;%WX>&Yo1{TDQPEK0nRFrMgh>3yf zp!unb_7Gz|etvhUHBW<7CbGZk^5d0#AG|6`)W;trL|m+ldfi=f*Pnm(aBbzrh^mIh zrADtkr&4PLCs+h$+7o8tPT4_A$s+c31N`x{m7 z;v!6@&vxHB9%zi;!S8#zW$*Q-2XFRe4+-k+uSY)u8TRGOq2k{MuPDeb;`(it4PLmg z&#|_!x+g!+1ah7y+V(Vc-I#o{f6ppYl7lXPqWDA2*-uIBWhD<^jApsQJY8B5 z|JubF*vl+_yV+}<|yl0{V4;ksL@U4>e0p!EA& zVLeSJXOwa#JtI$6?r0Mpie+?c}VE2rE^1Zm~l8y~gvC~;WF`+~IM&!1pn~sH2Ra)88 zhdWw4@JF~7#wCZ}W$S|Er;%I$OG>IM!8KqaPR z0_cXhF*yZ(+dGVbek1^ya^^Gu6?O`HtL#)mTVzK73ZTkXC9}4!(JLnnDJXz$EH`W0 zIh;rJF&;WHfMkS>fhkD<;u_o=KrWd}1JIG|NE27C2;R_?uR#Ii33(EUu;P?Yxjl^p zAcw@k0F+@eid=s%?@;8p4~jv7STId7W^m2e9k>Ps5a;5y4%1CPMak<8Bn*m}A{GV- zCc*kb3#Ct?QWiEI$=vosE{Z|5M6FO6s-h3oiet^ckU2B}WmK7^%ag~gUX-HWFaW6} zDg{8Cgi`>dljsxx0R_-KRKSUhhb5o@5)wk`{kD9}D=Wn;mC0mU1E>HBPEhTQoX3}> z3s3-oCE(2)d9=94a4!u7&Swi8F1k5f2?KL;;inDJQ|n zvNhPH=nx7Zol0k~;1m!ats2BAfD{S^WY!OF8aj@eh2d1(I?F{)ex^asi<|)@ zW)i`<|6!EmcbU4)Z{)Z#pAW*w`x4AWS79nTfNF{w6jgW)q)4yzq5$%p`R-+7As2%X z;{XaE*N&_B>yH~fN}HR_lE|)P4`WN=!Q0cHwV@c~+41bFr3PR5E{`~Kt!X^#8rstH z=OuePNdPhg3>koOo?Kdc=DUhJM>FEhwH678FRn)EQ~s%|cm5;yh}A9<1_@`v9fK;K zN_Z;Dd%M4Jd;$efC>J`NUS}wzpGkd%0!XD&*%w3hWH$y5c3WVOVneaTAVo%Tz#x@F zl{EDS*5|eCJa6W2sj_K1u{I`y`j1~p|0f2ZhNQvUxwLmCc(+`rCMObS8h!A-vVJTwxdXFTgC#^CcztQW_ zc|wtGh~M1lbDRTJvpwxxg49}f2k&XJ8NQB6g+y#0K*;@lf~7NEoo0DxyxasjUg(0b i#q({*)+9>?6HsvrIt8O~S&R5qY)(XM_>zT@$o~M@2G;2S literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/mode.hpp.6A926FBEE534F2A9.idx b/.cache/clangd/index/mode.hpp.6A926FBEE534F2A9.idx new file mode 100644 index 0000000000000000000000000000000000000000..cf2c74c8b009180a3859096d38aff9992ed720cf GIT binary patch literal 888 zcmWIYbaN|UW?*nm@vO*AElFfyU|`^7U|=XNDau^Mz`(GFk%6IN&eDmy`3@QIxc+sm z_@LmX!E|nG7f!I<__jTw+lGzfUrGJ~0n1cR<|`|9a_Ib)Tx2eB(ar74>ixB=edqta z9+%cUFRi#TH%W<+fkE0?@Yc#`V}EugMt%l1ZZ>W%Fu}kH@)`pJBO9Z}vPv!f1NspH zOnd@hF$Ok%c79$44u}c{ojbRWn6kB>;bYK4vfhmw#FN z%7X81$1*-9ZUF{1F<~)DFaekUmVC#;L&4vhkBLiwflWkML;_5}<^P(WF8vkvUq*n5 z2WGzzyAU5-z9==VSR5&&m?0s=19l}38y_SSm-1b8MvOdcV(el(sn{rh)VmpZ z*tj*hjbK5@$OR6%?k{dToET<3W#VBIk`a>UVH0N(XPrE++PLe$fmTKyHePjJ9ayL` za)RA?YIRX`A^Yy*AOl6XrC?#o$OI0{+udLEt4en^F!HboDGKSrLX?pU9FnjAWaI(| z94x#TIU(Uxl$w(WN*BdN$;H7S1~Vj;fB-9k$&O%hAehiJ1OjXZvgUX0Ywav!<6vTD LVP$7vLMQ_OPzTnx literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/mpd.cpp.7FCBEF52ABE61287.idx b/.cache/clangd/index/mpd.cpp.7FCBEF52ABE61287.idx new file mode 100644 index 0000000000000000000000000000000000000000..7d6d83705a258ce42ef152a027165062f9050ef8 GIT binary patch literal 6492 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 vIz@L5GW)kn(e_U0)329=+u7aE zUhatD!-rx3UYbQmn%RNim_TH*J|Mcv!gat+W*5uY4-%MI$^f&)QM3!mKoC^tiq^YZ#jYS#3UCY`c zR+r~otP~vMyuXE2KX;^W?|r~uFy)iDzT;c{6Hd2B6n6?;OUN8Y{fX|dVtVu(=60kLbzXAN!@Mta_f}mpwU%`Ylc$Pt6~(1Rg_kq46V}$0OVRTz zLq>LI`WM{HSyfQ-U8p6*e*1oX?Kb}aZq|Dp;b;ClG>l)AQ0Qo0w{fS|xpwrW0p#%g z_1iXhTZh*8U+P#SjlLpvwl^)20Oh0CunYVBuvcTtG20 zz~)G^L<8WSn+yPmc#t$CJff;pjbI$WLWsc80NjfnV%Vo_Vcvn9j4lC5GQhEsvAt<1 zXRt3f%q{8DFJC|=8Q@5LWE2ghPuy>eZ9h7HfPf-pfQ@>ik%rQ{Jq7tY?Iq7d6f6Vm zR68{^ls@m*iTSFHsxcx8kzZUhPFJV*Qu?u#_oc}d?-YqBiuH*?VuaF9`02E6OY?;U z5%pqyl8|Il`odE!Z?1i6J}RJC8Q^G5bQle#pZ03d)!R*5z71BZRdVMSn zr7w;ze|%;@%PA4**!9~5d*JtdhtH`>m*@{gAjokyHd_5IZS z0?(&$Qr0WZBMqG*GROeC1$Q_NW#4~l&662zmCYjREd%TlT;Vj7{%Bpn&RqA_UqmFb z^E(7b;Qi{bweC3kX-uYwjI8gk?jNe`U%9hq=F&~RTLKD_0hXercp6H7{RSDOfsdDq zD2(kVtCIuoPv=3+HQ%nvCIhm{0NZ$*hK925^Uf&a{wGbvQ(*+V(p?z@yVKnn1bfmv zo{&1jAAMF741@$*cuP2w7sOL>gg!NbZ5o>(8EczdH8KYYc8X4&b@|QT4_o3_SG>e&r*8kj!FQ+55)5AXcA(Xbs7>>1?APJer5TD3Pd z8xovsNVYPHkf#I(qtO{77bZKP3hPX|s61}ikz%h{qb69d)rS#m(wf2uw)V0b2)3*3 zT7r|*Nm_y(YDZ6+9r4UA^j)lLFT(9cj-U%kYIbb-RwIMXL;6Ew;vMh&U9xpPmrTe zWd8N<2l^&_cpVa)7M5manjlXVF+7l?XsUBpoG))WaTF08Y6z7G*73St3=rffoYnP} zsVk2itAYeuZPrA_2Xd4R(*k*_gyDcZMZqjUjv8p$<(pWl{`wRoSO^o0=T^8DHeqrx zNQGVTt`rq^$GcNh*kkiJ+e0$mT5Ze!5fbdmbY(L$kfT-@5fx8q5V}t2I-%>=mWkS@ zSz;C>SmLE9Mh0?}3KIg+3mHD|oWh literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/network.cpp.109ECEBB28F3CA1E.idx b/.cache/clangd/index/network.cpp.109ECEBB28F3CA1E.idx new file mode 100644 index 0000000000000000000000000000000000000000..5f659f91839340819147bc27ba2dcb52d4f6e1b1 GIT binary patch literal 12826 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 bVZ;m!1(`^s#>#%C8kM^5tls_mL`eP*i$DzZ literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/network.hpp.959179E628BFA829.idx b/.cache/clangd/index/network.hpp.959179E628BFA829.idx new file mode 100644 index 0000000000000000000000000000000000000000..87fa996cefd4a53e6dc1c454aac85f53cd8b0e89 GIT binary patch literal 3296 zcmYM$c~BHr9tZG#4`k@(P1EwaXSim7ff??5xSIPAIXqC@s8LK5A>I}Eb&@JYhANz5F%pOMbSk`#kgMUNr>VR6j6g~vgI?cXa4xsujX67hU)40yqTDr zoqYu{Oy=y|+VGpmNiv>5i;lSgXD3G zg^xa;t6%$`9shuijpV_(nw?~AF|a+0ST;+;ARS_+01Y5%v&(B9h9mFy6{)byLzb4%;ilrx)p zBlNQeK1f~FcJgHV9o>|k^|NGCMdtGZiyYT%aW$_QO781xzs%jfs3|_W`#`7L^k?-~ z(nr*-YU^HbtR~gveZP#tH&>1|tr{w>-F9SycYWv!YiwGT=f^JXx zmM}IGvH3SMXMKGE7nC&JeyFi4_;!DDYuKs3ebaKSCD{o-5GS2mH}m3w$GQ*8lRrCf zuevhGc*GuxoR_T+Q=PkQp2&#py^ej&C0ZhG9l_JbZFu=&VtID*4?zI9Loe zC_T>W*wq}6{yh&(GJ~U~Xm=8}zV^baKMaRX=HW=0!Dh+qNkZw}H&5|RC|j}K1^UtJ z>(n|orS~{^>3Fbe{dE^;q`h9P*DAeX%WjL^-xqrckfVKs7@=2sqyD2eiRpU=BnWBm zu66fT`k+%~Oxue5QXYoL49?=SG$fQhrZTcW&aQ7J4`XBor}1eb38jy9J7&mzvTB%z z&N71&qy!HVN}pQ#eQH<8;1>dPq<=q53^OWyn(IA05 zD+Cxp`#>?!_W6vRkp_R+`;Qwu43!xyOR_f!Wq+KtF=_mQ)LIp^OJFc|z-kf<`|k4v z7>sMX^(?<|UJVb`GK1r#csCNt6~=G)wzE3)g9sk_%M8xoGb9p9Uy#4Lv@OIC%0siv z;8-cvorKa)uz!CKitlOUAuBU@6h8`+Q2OcXcizyq4ma^IPG)evCf}EY(ii1DU0K~# z#tYDg-T^<+&tK_FMAJ>z*H60z=u7($F~oL6dE)J7V9w{I>-!8kx~LkDEs?fl--)0`|t!0d6~iK ze7ZCz4YZ^C5|mh_Z%!J(J8!Xb z1sG3ztxBs_`p#9Ul~JcREfS!C_TgfBJCr^NFQbY$7NsL^&0#J z2{4KF-lDhd`_tEcaGHJZvKRr{(>_QHGAaB0S#>Mp8~&Bd!vLAVnS7>1Lg}B(sXIPq zNzpGn)XNOc;d5L`D7~em)EZ^aFm{%Y6B9c6QUSu*Fq@?Z1K`Mkz#SgwPOAO2Zv?`| zAY&B5Mzb*qVNk0mVV?tpW7rsxo*sZDrv^PN0Qc^s`2B1E z961x{Q2H2IASd#&lAEL zI}Jy%1sp+^+6p*gDy0-~gitCc;E0%o?5L9qON6Z)!jdF;ARL?)oQ-g}IvgV$85$Xl zaAa=ec!bkbX=;SiRq1MkGgKLBgfmr{YJ{^?S?WnW=|z*9tE~12`K71d@yOk%XM}LyF0sU5cY8P&>%b-j%KNnfF(Lo z6agR@Zu`^6(UtA(>jA=2kQ7cy1T3MD3J6#t9|aGv1Ul*);D~FKGr$qXs9u00YEi5J zN06d60hX9VDFPfJh{^+4@`O-m084JSaIz_qClBV-#!YT$X@m&te02T@ z>%H_wY75|qDU=ex5kf2_3ueCkO=z_gTdUp$2h^ zVy`IcQxOFl3L;nlLB+nd_YDty{BxM^-prkubAIJaZ13>!b7mY)_tf6g$ERhaupACY z$l-9NW=t8=m&@TK@;RL8d9wyqPIwY1y|(Yrzei>c+$=p&Gj{d#oi@dlLW9ViE?>j} zMPPU(SGCA>R%zYrzDM%=O>%}O*FN2Ac;jDWtX;t~&i-v}WybbT zL*0K=GdC`F*&u72fUN!mg^!nbx(;hxA$tzv!KIlw>`HYGQRc3FVlzzJKSH1h|jsG~j zxmbB~n!ewhOL133mTUX_Z_YZ?QZ-Ih`6hKk>DiGJq~x2mqML9}LwQ;k!S6*4J%4wu zDU3{;x;bS++_oeLUirvo`P%yvpQqehpr|SvYEB9#1a=GR*6*)ncRn@++upCx)x|F8 zS@OBv=gQuK=9X@mIi9?Mz5Uf!+FWM59{J{R+e~4E+a`12IP2g~8O^WiEVnLMo-jYG zvOHj&@16ZF=FOQCu0@GbP9I(I&Mb4qr0v|v(_-?o|5F~YDWxFaB;D@#rr?Gn+68NZ z!uLB>;F|co2gdDnsQxnVgPXwy&kHx|eeUm`cRIiNYNO#{P1~Ii_a#o}hc1skRNMcr zx8LSHcJcA$d=J$(pE&57F5OQcD4XxQ;_-Bm--(C)ZU~EKBr6Zak9q%ZR_^Z?9=yq0 zD)6Z(Judw+VR>!sYv?4b3x)XU3*!wZr*@lMH>o2ynC!YZ0IqmEx>I5 z%-oIX+me!ZY?tx_3ch{Ks&4F^8=bK=Uh;8+hxPrD|MXI~zccyRd*Aoe>yj%`?<%)R z%-RlY*nH^s5Bjg-4h1e)5axeVukiM%Snn_Q?PuV4GuJUGC3o?Lt6`fL#6KN6 z`s=*>$1@GjgY3|p1BR#jOE2E;Q5hTWv}duqkH4ZK^v_3n6I*UO`N-##ww>1NdpXdKfJ)%$P>r?@!s;N#ZT zCF|Mowvw6;6B`l-4;ZugeB9wX$)>FD@K&3@rUb5@|L2D}B@qeEy^SK*e2fficRyIy zjlB4{()azBJ300R-$Z5Wj~(SqoiToRE^s*A7cBnTzVrSEV`fk)2QPu{5(FkHqzX$8 zpF)ts;q;p@XD#nr+zT-?ER}N%6mL9laR_mXRWh)qQpp|`t^Jf zKg}~_c9Daa8J^0)Y;CrCqRdG4|K}^0PO2DN=ouzvhNg0GBWO2*OQO_BYW{166I{Ec zJ4XEzAZC(MIamnVLU2noGcr^BdgF9m-t{d({e#3zaw-QGf_5ReC7K$U{{M{&iyn+A z%Gy7~m>HbP!3b@HTcTVl*Zi8{*|Fcgck6#*t}&C6%E1cIR)9mINM9uU^~R?j!9$E6 zT$7qH@u?hSEExMlAtNNeo=-^|In~pI!-1;yFUc>CtpkD^A$lVqw;S{wanP;@5S%2Q z&9&Zr@&*u;@nq)w!Iwt7*&BTl2v&lA zB^U}0y}sUM%lBtMa7_O(Xy|?QVq-U}^FT009%Es+t6xcQuW{$6<0bJ5f;C`Q1Fi%cf!7E^8XX!zNALy6UVxfl6G)ptNw66#o57A?3rJf)X(U{{ zZG+(YJ%*qs&r?HiGCNrnHWz*1aTy7GmO*r{Xh z{9KSqidHW=ZvLL`E#4{K)9isqmX9>M9$3^mYjv{lv%crbl_4xaeV)FEV2XQ60Kpvp zoKS+>fWHk)WZ&=2{r=HoK1-1Bh=d+8e+8J*#q(Z(gf7~s8Iq<0R75+dlU7a2* z_nS3GbkyF0N6^{BIf&qBx6%Ftvpuqd2rdNOLI|KacHIbm1Peh|2m$h(9Q$*J!vkm* zTx7xW-1NI!ySLqy5M)dkCFv*|g4$4RAA+_fwn~DwYFh__&SuUUf-a6OUIZh|A~Xa? zn~l~G%r?u`5S-;W%Zq-|Y$0d}E(XWN;6=Y|wvmoEgX3m8E(EheIz9}Jhw1ndSYM(E zs{peK(8x~?-XEh{%mIQcq1Q@?Qq?}+cmJvIEfDkz^Bb&|ul_S2*T#S$xC;2IKtQkn z_yr)aG@2W*gnzA`B`6Y!R0K^$rYeF8kwQgKEmEs2t8EfX+G2hHJ#E=GP+6UrGWzA5 zE(^IViUcAdi>3loA&Y8(T4>cYr0VZsJ)#U)%mve2Fk^8gC{}`!#hqZZ6GSZT0+U@J zW^p%2c7v3~e30dXIg3RgF9HRNM?iT5R4hIO=|eEJiGOnGLib5KfbKuC5E5wbDiy!Jo>kHsnwSAm4ZS`gRL*Z_tNKv--7 zaT6W4fVhRmRxoY_6WfrjzQaFnPU8}^GqE!zsFUj~2nNf8EgS=KxJUM89$*MgWvAK_ z%wcnE9TObm6|xqJP+AI(3kr8nT{tHTSbPtv_h99muzAqVy%s|{4%~1Ri0FYE7J$fQ z(Xo_mpSc-8a3@H1f?8*PqcMHyX(15Y4WYZCr><*Nb5yqpyMUn9TI=K?d$6aw&DA0x zn8KvkdaQ}^t4bXA1qjA5am3@?_!9HlQJ?iF%sgAchu|x)eFY8#UxV#y8sC8J8yefe zwjCTiUOu`JX{qvIXr)-L1ScAURzkR^c!y;d?fA1?f^mvC8-iPbzZJv;^B^b>dU`Lc zvNRqrAP5NV0O<}e_tmTaR`^e>9T4>5dD;8NsJ}nEXH&@#6z~Kl1eXJUIY|65e_ULX zrxplyGw7xc_KdSiZ^O0WAd9;InXf@m8HTnBA%VITXszJ-* zUC`X6u?{qKG}eQr9<)ETl@W9bE1e;%9in^uS#ov;uS5?B4$%y;2~Uf*omBJiBd}=Y zXyw$a>JP>EPqzmHiwc24*k_pYK{Zpm9SBa5Px0zA&2yBp>5(5xuo{Hblr`^y@GgyY zAgrUY9)$Ivj=E`F*E_85Vjy@EjBkQ0=2+U(_1-U?c?2WXk#+9^L+tbDV_N@Lr7nxwo>XaQOgiq0*WQzM(_ymj)0=CyW_>A_e*F&jd2?j**8;~ zQlK0&9|*b`xT*Uk1g)rWH(3evXFf|H;HQgV2zoht`3gFWL~Pb|@?giS5{6(AsEeo) z6oal9LSie(wcy8T6p_n8z8utX20c%YPczQt6J)%Y0D`4pQA$^P2@EfR3BfYZFQbZ5 z4%%{BhPS}r7KOuYaJ~&51fPTca}dVw9q;<-L|`+^Vt@DkT?xiAu|gJ?LhMo)#$q1y z&x7Oy;Tod?$F6Rjq?T}QLx9@{r%|+eEUX+7o61ICeX!022znTJ$Z?=|`H%j&R3j~T z7Mi49YS(&;MaEnTGyZN`*kvFq11o~pz~CB4lKNF$?pk&I9T3crXZR#Nia0pDVt^P3 z_L26{4E6aE(LSokj!R+7-$^(03OrtcFX;q)a>|x2mp%IK1_~VWt)Nc+J@w;n&aKJ7 zVhxzpfO&GRfnJ`k{UjjhDsWW~H_1_b6(6(!f?OV#{4`>~bhxk)A^Y-Ykl=ltSxXB{ z>er%P<=`ZXXZG!oiP){fn|DnGaYXik}8&`y~t2wmOYx{jf4!~8Op>dWAJ8G@$#5!N>U z*1}RQ!5CqTj9{WLQATixbclLtR-C!nmO|)oDEgPdIKxG&*F12|ST3#C;6~_4J%d*h z1W~Wx+5$eAFCzl?rO!Cb&;*$8rX{i)ymv#l=@;u8v)Ib(Kx;=_3bL$M!X>k-Pv4`v z@zDCuJow?=kQ-UnK(LEJ7e)4*4O`~tn^H4HWqj8A`YPy)Nwp z_fH?p5L7v-`~@9Wcuq?~#a&HJJ(sGDrV6ZRssn2vh^mgJ7Odu(G0pYccJ={+5xR)r zpJtgMm=cjPP|)F*mpBfZc9?(g8$)mlD7JtLg&eO4%$H>JJ7Y73rvQTfVg5+b zz5w+Lu%s%gY@#~d40g?6Pt{b}0;-K$msdZlE#Y-8c~ieIr;PKFvkeHcJXSm!FJQstnv$At~UcgZ=Sbe)4`?Z zJ?Ac14+IDC21z%6DI&MV<<9Pe`{r+w3(3})$j}69?@~If2W>sLZSAqbxaaiGe4rF0 zDhEm4s(YTP`&3UlRi@xp+vDkPo$ER(+2Jxc??2$wnzdWJ3Rr9dX&cB6&cIg7(_0Pz z!95_}1KK}iS8Brct{Op4G0ZpVP-?wZT~zo>AUKpaR9c*)YtSgCcrh$40o@YtU~vn$ zZUJ5Kea~e*-{=0VPms^!3kf10MFf-hNg{&Vfxn&7;%o4F4IO$E&<-I4--6*=5Fays z!aJL{_)g~+k4?L~{zT^TA)Q=R8oqvdVaW4)LKeBETq_p2_FPXEj{|$0dae_|o&X_> zCxJZ)0v1mJdy0-v1A7{bSv&*m88Bk;EU;(6fW>pbo}=%b2lhM|vUm&Bx4@3Y=b(NL zj>j$M*IrpP>LNq+MstLE(WBsT6ngw@(mcS$6t_pDiDkV5Vt3bAM1;&ymG zT^7Q!_zwvG0TZ^9#Mw?oWw8+=8=>F1Q9h@}eq6STPmsss84;8)5&=OePijO^CYM2xda3=*-ZPZe@q!XS^wk}=6{Mo@G~%< zfq%_5x8nKM#@j%Pg?S5tYsDo`x1?>?06`U_vaFcWlG`O=#%G3LJ_z!uYLtOZ8Mxn0 zNsiw-@=XgvhiR}a4RSY!UV*8K-V zu#aIMF~MkIw6wu>O8LB|-gOMYrQouZ0xu8j^1!>{nZht##!%NS7K`1Uk!%}-v1{`f zDkIuzaHjI1tpn$0;-l49H5;D-L5WbJ{CUv;g3eop=Q)P8lE5aalLIXV+P(~Bl0VcW zrU8{1!}p+U)sper`?8*hS^z}w2xm3Axk0r)m48%z0aUE3}r8;a!PBr z!Rt2oz1j7s-TJ#tS!XBo?)9{^5^9T2s&EWPnG_!Z+)MU2?P`6iOL@%KR7R5ee1pfK|P+HgrJxaiwH_o z5=Vk|jGc&}otd3AL7hM+BdF8rbOeJ1!7_ru+F;$3wDc5uTQzmch^aFAPh!XA75xKL z{pSwJ&JHYvM9zrusp$zE4rjo?fe;~AD8qw#x_5U9aSrv?xP-a-d)WHgS@{LpJ6fwP zY`Utn!2vxiySO=Y^YZl3nfEp89oZ+gm#LnFF%%jReLh#fV-2LDeloGKNz90-xaj_z F{{mcCorVAa literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/pulseaudio.hpp.C6D74738A7A6B198.idx b/.cache/clangd/index/pulseaudio.hpp.C6D74738A7A6B198.idx new file mode 100644 index 0000000000000000000000000000000000000000..40bf5907d709a90dfc20cedeb8f98395162dbfd0 GIT binary patch literal 2202 zcmYM0dr(wW9LLW&tK74fbC2s~*(bZQ%U+g;fUcsX_=3bonjm97@-d>w_&^j$5ucc* z0wO|+BNUpITKO7dEW*S`qxr}vQ<~CCqP57;n=x*|MeBS&?Bepy$Ufti^}92y zK5x;|oP-5q_KujBzy7bj8KDoxzsSD+?qtJqPf69~yvI93HZSaU`dVR)d1iGtbfQtKdKh#|aotl{PFgo?p(0!f>#be9n_P@Pi z>)6D1D(iDvM_g=5o|N`vWlMFsyR@P0NapPyb32aL-84UpP;jgwe zMay9nSrvdIX{3b(z+GQ70Kk}VYC!D;>u`$93c!Ih(8$8eJL~g0+xu>y$ff|?oA$P{ zP`SBi(T(!><{JWnE`WV~e03~<_SJrfp+~Ol73)ktMUlS(u#-AXEYuA#Z`wN_)Vt~^ z3QzzJqhV$iDj%|{!&ryXYACWR07uY>UMy4|zj5>PCwp$T=}-^l)t#_KZ}IX8R%e|) zB*RHjcLm@m8s%i6&L=KjSL*X3xk*NWd|sxq_w#3rwnyBxZPHO>PylvOmx+ZsKQp>x z)RVMh!4&x@00+}xKNc!a8F#O2t!{{)5e4)86j6=JQwPmRd}J?=iu$tz&dLJ1fzTc1fdFMCbRL}4=$3kS}_=DF8dD!=Hu9t2M#-m1*CB~|C?kc02kSBU zE+L7i{N=p1@XgzQx=E3&0Bkea6c#GaOr4iSGyuSBPda8bop>J-tnaOlK}8Q+%92`g zA;AGCz#x9nawT?m=K~GF@<@3+!GS1HBsdI(iM#@!$kgY?Ly?)z1&1ON+dOdfx*k<- zn?SH^kzEA)+5FrD>&^Ngg8i-j3c&`eK_S>^Gr9@3BRk<0pb?q+gk6tds-cqWt$XLr z#4o9Y1REj^v3*O%4ZJpGVFx7GiJXKt0~A>!@FIXBD}X_ZKRq20b@OjXunpOI@*035 z(?9Ot0jnBI8%7|49mpXP9FD?8UIkFgDuA00MP~ejKA~r4v~H0g!LnP9=iWmt^F3KR z@??6du1F+UGD&uVEt18LV3**M2v!6|A~;wGmI!tWZi(O!Aw(iLLWqzEjuav#f}?~e ziFXAQSx4|{fFi2|-UJY`2H@gDkqOT|ha&TvYYnwb=k*5k@a_kD-hc#~9p)%5HPkYh zxx-Lo?s8?JmZ@6$;mUsbwY3`|!BT|e;le_ZiOT(iBJ+~#2(?T@ZXeV#>$q%CWRh{W zpq4px@$<~LmkQsUhXngtd;_^zP-I4Nk)X%~`o+1wWmn^)k0HVSk^ZqiVqAIa$eF2ON9oLe<)cgG+pTiKG<;Ainx3=z4s;$F6@C|38Eu L0ML4mg7^3z5~vkq literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/sleeper_thread.hpp.B273FAC75439EB17.idx b/.cache/clangd/index/sleeper_thread.hpp.B273FAC75439EB17.idx new file mode 100644 index 0000000000000000000000000000000000000000..ee3351c34d12016680e3261376da49be2729a29e GIT binary patch literal 1672 zcmYL|drXs87>Cby`u6tq?H#_-f>7Hcl`>Wafx@T~#8#B6j;X`xEU3e~6sI`iye@H$ z%*%AnVVvR>bqOwt=;jbxyFw?%Qd&4ok0a{hSoCcot5ifFLWr6W60EAMxJC%M$`P`(u6E;v*=MtG^wZ>D-m2L8;ru>j#jbHl z<_nGYCt2=ZY2A0TYf+RTJ59DS<^IDjn^$$5iAw)Af87nzRrS1T-8=5FB}$+2y8Tvp za(#7MThFN%>CJ0)kGQ(BQBZE1h+9t2_3rIeI+Iie2rt$o}gJnhTfP;$?N0+O1!e z{4`Uz6VSh@_&yL*)2!WRDV=)w(Tmc?s+ZoF`Ryma`^5i`B(-tJ*kJX%0HuVaT`;aF zUG(o|fpP)?4b^BFh@yWLgb@GXLkFJi%!<@dDiFxG4_jv565iSk4ZLntr5>`J&Y<%C*J zxo`r!j5jh6<-q$m;1<8qW!6x=Kp@YOHLhdvz2A0)k#JGlcR@ z#XGH%k4hhFs9GRUWGNcT5Xv`CI@h^5Yh;U#Y6Jo{xlP9q%J(gw+_d>hVX=vt1OhoU zN5T-w8;(DJHNteec3{IuQOgj@ThGO=9#xmpq@gN-K!K&e$`H!`w0`}t{_oQZHB=%H zD6|v~Wf;tZXV0DfAor+_>I4F*@>CT=D1TaSdEUE3`5vW)uz^1Xg+U>R8zhx8W(FM+ zLbz!^cX#dB@IHs&*Z2*JKYpFuT=eb|il7PC3_%dAf>Rg1pfcmHF})PQmS9UkaK7Q3 zGGtRAXUWOsuSpQxk!~x3C(>g@D3BLmOw!h-qQvX~hv2n%<7`hFjvJosS;HYX!W>Zv zxb#xDqH7xmU_z5xRFUJEf|;^A!573)ND=Hxdl*8zGCoYyQHtPDIgAJ{mCJ~b49SR) z37N<M&}UGI4RFeL-t$9o`fJ!L5q+h z$j0!E5lE5qt`t7Qrv`t9j<8s3b?3iq-o| z(}IZ|?GzzdooqtLRA-u2t@SQG)mn2=f?$ib#UnUf&NKw4&pH0%RTn3J4S zvna3}L63SPf>~kKA~;eVUIf=jmk%LHmZU~Vm!+$DCb#G`$1|6=pIy7-lDqpn1#sKk zu>c;MC$=tTVuo!)^4k=_t#XGWcvPNnQFe|p$qzgkvuo<=gV!j6$!Ibo8dq7PP33Y?)T9&S~qRz}5v|(wOv(pA9eewwzzjA^3E@2(cJa z1bc)%8X-O+J{rM2%$=>WDA|tIawb!IACAMR1LCjYr6kWvF@9FR@mXXLUjt4Wkv6Gv-e3 r?@+L^Ea>kaz<#UahmCQMK`NEW6-pu-XjuP|4~~k#Q8_qD2gm;bpOBqk literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/sndio.cpp.1174277772D16F52.idx b/.cache/clangd/index/sndio.cpp.1174277772D16F52.idx new file mode 100644 index 0000000000000000000000000000000000000000..dcbcf66a93e0bc7fd46a9b8fac102640380ab6aa GIT binary patch literal 3834 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/sndio.hpp.2C5698C31C5CA7B4.idx b/.cache/clangd/index/sndio.hpp.2C5698C31C5CA7B4.idx new file mode 100644 index 0000000000000000000000000000000000000000..a6d18d34376287f1e1ad737d131bb841036f3a23 GIT binary patch literal 1304 zcmYL|e{2&~0LR~Z?c4RWch~FMyX$R3Stk)9gv`ny=@@fkGoy-RPGcMd!F94>ZEV+V zIDgMeqLoodAWoED6Oez4h9;wm;nF+03gy3uKNW5&;tQz?rHDvg}y7c?7ZvU^u)eo zwXey4B(^K(fm3a}udKdSdoDCI9UmBI+|^c`;QoN`EbcSCU(YLL?e@YH_*Z=r<^!xE!gI|1j ze?ejDWA)pyO{ZrYS1q4zPIp}^-}zQw_kAUeCob-|J@>0CBfs_CHMQxH&jzG8x%hIh zvbAB)$c<;}au01=l+3(dko%~A^y$g{Z*1b9_m5)3GHIawvcfEOMe9SoDJc0OYL`m!B!PB!~zp0K4sO7Y&22+y>C-k+zhE!P_>Ne(U+Y_%8`r+4>I7;WT(`&EeSXRn@&5f-=A)gV<;Qa_{>P!`&x* zmkv(Kyo5|_1sCU1jTQE6pA|0rynjeUnF_$NA}^p}@GjqsZ!L-TOUTLQ3!ESrygNI6 zVd~9^l!)vK!0H0kL&M;`p3t4dW7EA7B5c0IN%Mbj*fI1-a{HM~6{(E#h&LJY`~A`9 ze&{=C7LiQ>SdkTthQW`|CQ4F!D;^e+pa7i3WeGG4{?$k4uO6S%azrHRC+ybpXc+wL z#JTzvr5ox*TAJJ#7a)&ii!8S)XlnAnAwT4?_~JJ@>ogOh_*{nH8?ll$;ytGBXvyt!%5ug5$L9Z|!7z{V&ZEm^pyxGE; QGk|5DaPyD<-+6uf2WM$TssI20 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/state.cpp.E5F8B9AFD9E7ED3F.idx b/.cache/clangd/index/state.cpp.E5F8B9AFD9E7ED3F.idx new file mode 100644 index 0000000000000000000000000000000000000000..ef7eb2abc55e669d9c28fd1c9419e5096bfa32a6 GIT binary patch literal 7084 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 Hh(i7cL;DM{ literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/tags.hpp.38EBC6B49867D962.idx b/.cache/clangd/index/tags.hpp.38EBC6B49867D962.idx new file mode 100644 index 0000000000000000000000000000000000000000..17ded0c57ac63fd7e3000ca8fafd49e65b05e410 GIT binary patch literal 1076 zcmWIYbaT^TVPJ4h@vO*AElFfyU|`^7U|=XNDaw4$z`)?h#K2H7XKv7GKUPJar|mPc z?ri+bd}rnk6`n8W1S(li-Fba1e|n=~TyoO7JP-Nh?ccV%iPUX9tL1HW+`C#UkjHh? z#FbIFmGiSH4>WQ+$XSg+2wO+r_b5rsDyPaiX%pa#HyUO0( z@~`nrl9-(KQm_BZw>a+35|Ep}XeHlr)1}c5s+rB5YSs0enHH!$-F_-+-vaKvRp;fG z?M^%>@GGk!yI}Icl?i=?(iyq)oGZI_iJ27re`&le(x(5w1@Aszk887&+MkC8aBtJ) zZ*8@R5`UDwMEu{R8~)3d-v9lwhN0y2MJI;h%G{(RMg|5&37)e@k9>*XV-gT%;1pFB zH3Ab19H0PVU|>*FW&6ARVp1O;6O%9lr-ZbGJeYvXd+gaOthM6sBqk<)L5L1cE@>_W z22QB{&=vcv=6zOI<746zX5f@ok~RPnaQ!oG&vEl_GI=h>#3&5a&&R~a2A7|ub9$MA z)?P^gCSI7lFuSlgTz;-<;-iUy%%k7!Rw#37G#GIT?#m(~5l=85p?Md^l?}>)$g*9!?=?A!S&=Gjf3gTxqF- zX4{n~ZHzpe!t%oEuwZB80ta_6r|aI_RDnK59!@?XK8X-NA@vs%ZCDw3IHlO7cwu48 z$OR7DQuh8gPPy0ln0Pq(wD@#+I3)xmgeT9dHtsrbpcQ0-EuRxC*cmy&!3_&#Mow_( z!UC0%3mllRFl1x|huyK?{vqGqei3El;S}W*RXnd6y)aL8g$E-KrzE>17c4*-xxfJl z3qM9qaJW5;ye;wbp22@c9!?$^9%WeQF+)O)2kaOgPJU*7_J2)$n|RrkdKq~*r8%V) z{&lz*ggXCH2YFdoN|K={H7602UW$v7i-SQ7E=cMD0S*L{9l_*AFxe1HPOG-oEkQFa Q{<3j#vvaVrF|Z=!0ArILO8@`> literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/taskbar.cpp.3B871EDA6D279756.idx b/.cache/clangd/index/taskbar.cpp.3B871EDA6D279756.idx new file mode 100644 index 0000000000000000000000000000000000000000..da30b7b2c157c04d7033cd7cef0ad65e3ca015fc GIT binary patch literal 12886 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 Md#0hVzCHT<4?Dw`DgXcg literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/taskbar.hpp.3F1105A3E0CB852D.idx b/.cache/clangd/index/taskbar.hpp.3F1105A3E0CB852D.idx new file mode 100644 index 0000000000000000000000000000000000000000..f16ecfa1d5e8f9648a60563aa451485e7cfda18b GIT binary patch literal 5366 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/temperature.hpp.58F6012FF8EB97C4.idx b/.cache/clangd/index/temperature.hpp.58F6012FF8EB97C4.idx new file mode 100644 index 0000000000000000000000000000000000000000..82a29096ba7c8af506d7bcd13a78c5738b044c43 GIT binary patch literal 818 zcmWIYbaT^UW?*nm@vO*AElFfyU|`^7U|=XNDazc-z`(Gbk%6INPVWRq?n4GVt-sUv zm|cFH_v-562ZbrIY20gdbGs@&ZjXLeV9O)(L}1eQPqs?yo-nySk@4&~7R1eP%bIDu zPZx)I%~y$oHdD?rAN<&48@bZ?#-g&i6Vrq~Fx|{Lo1>%R9nvHF+QZ_{=4C6wI#+Fa zx<%p2y8ft<;3}t58`KLb|H32m)zZ{fj%Z4W^PGtc^+nAc41za#~C@n9!|NSt#dJE2QMQJvj~TXD9qc;Tww3=fX(4y z=3(aHgZY<{3GC0w^Qw)z4jgD@*NnXuN{}b7kiT*_U|<>si>@m{=Itz=B}Fj$kq&m;j*0&!hkV literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/tray.cpp.02A8D890AD31BCCD.idx b/.cache/clangd/index/tray.cpp.02A8D890AD31BCCD.idx new file mode 100644 index 0000000000000000000000000000000000000000..d907a6a2994853609beddb733ee264a4e73b4236 GIT binary patch literal 2114 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 gl4L0sE4^6dit<8 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/watcher.cpp.793479CFFB135BF1.idx b/.cache/clangd/index/watcher.cpp.793479CFFB135BF1.idx new file mode 100644 index 0000000000000000000000000000000000000000..c0bc0cfddccdc26b3caab2bc6f40fc2adedf861c GIT binary patch literal 4334 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/window.hpp.E69A5ADAD0A0E8F9.idx b/.cache/clangd/index/window.hpp.E69A5ADAD0A0E8F9.idx new file mode 100644 index 0000000000000000000000000000000000000000..7f457c59ec1ca16ea0029b8c67d668407f6539c0 GIT binary patch literal 1456 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 I?+q3GA9=D%2LJ#7 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/workspaces.hpp.0FDE443B68162423.idx b/.cache/clangd/index/workspaces.hpp.0FDE443B68162423.idx new file mode 100644 index 0000000000000000000000000000000000000000..e4f37356d26c43375d9da06060c2ecfbdd3eb81a GIT binary patch literal 2044 zcmYL~eN2-_9LMjjNO^kK_GziLw6srK))or-ARs7$k=KAmor-LT8=~T5Ql}sjaW00v zDP)LaIHSYYIj3&EEQ(A)89GH2MY1TH3y~@6oRev)6BW03?w;Ns-~5tK?s+bk&-Zzz zWu~WlO_lBd#xPGl$__MtCNNq!>r>n+oORFCA7X9=HUwyoI^{5Gzq;BTT&zt7vk_yk@ z?hTgJ6`B3jx~`189Xr)0F0B9SkWB^Zj`+QK$H2#SuQzV3kIB&_G?&*YJN6vioGd=i z+7q*J*2eaR!D#y_|J=^Q&!&_|s}{XoKKa*{x4ie@p;@SgKft<}TB@ z!>y^`{&A~);NrN^{rc8{yvI4en{UPO2QOu2C(m?TSkUy~>3#gD^nT`X*gebjTFG+H z93%t4FW#J0^QvD3kIVv~3kS*rl_227r2_y09zM8KxbBTrTIA2TL1}PGT)XdZ`83a! zCp;k|p(KVii#1m&^y|0{n`D+!j2>?4qXFLswC*`+~ z+40WPT`J@!030fYM$?dZT5^2FD?z23cofXeZ`au!5}%NEIVLY@MH!EDjN5cJAJ6VH zTN|p67O9X)060txi=`p$pP#B59lh-GAdfU`f3PmtcRvf1E40Q-p5r_UWL(e*zWZJH zD5n3Ir#?rIAmbXOA=2|#JuBSNcJiMB9%&i3>a4!=)s8u{-__*};t^uptTUUX{pa&f z-28a|*!H2wDgdmv>*Huhyd$IYX8fkcEEO6i030rckD?**&a>0|&LsIgRUw%GutRi2 z(~x-Ank9YJV(BOz4Q1~S(S=wg{&UL7yo3p=$8rQM0IU7gJPp9T^b=y}{ZI@p{N>CR z719d;M~D&8G^8gyKR2e$z5UWq9tE*C7CP0ohfQb(|Dm?QXa@240 zXLC}Q9eqCv5*#iMC(LrlQM(<9d#iGnUr&PshXsUbnd1;qx0%vVK~-i3Lj^UL2@4fe zROTmCP%o38#J+c<_G%I&*oJHhW+&vRl}tv+Q3rHwa!G;J! z6v2XAP!cS-gy95RmDWIlZ6j@Q1l#3yCBgP^`$&RafvzBe-A1=HYqHjLdtJg18Nt?Y z>qvr~a;K7Dm)xbyE*R_G#=EK@!Rko$7$!VaP|=y+kfUBRogqgxX7)mkT3g~yop7T) zLxcod$6GT>dqas)6(VgDYPE1>2+oM#^36Oepnx`FJPsB3R(u%0jQwvUv%VdTjcmA0}>o64^^-RKm|3Q z2@g3cI`dnmpk5Pt4+LvvTIJO$uUKCC!zx6uF~T^SVABXwd~eQG!$42uzb|gvsJ31I zWZ~{LQ@@izg57erj&%VlXa}CJPK}(A^28-0*eo|I3AT*2BoXX(xreh>K<;H5iVKSJ zUwrRNiszP;y%^N=^G{cRZ&dh3xo_lsBk_$Y--sW^C5jcNBY#sURcc--58xDp{?h+n H92>xYDhu;A literal 0 HcmV?d00001 diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index daf113c..6ca06f6 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -171,7 +171,7 @@ const std::tuple waybar::modules::Battery::getInfos if (!adapter_.empty() && status == "Discharging") { bool online; std::ifstream(adapter_ / "online") >> online; - if (online) { + if (online) { status = "Plugged"; } } From 336cc9f3368a2badc78131df3fbf0d270d8c25be Mon Sep 17 00:00:00 2001 From: ocisra Date: Mon, 18 Jan 2021 12:39:41 +0100 Subject: [PATCH 167/355] . --- .../index/ALabel.cpp.09E9F4748A3CBF6C.idx | Bin 3224 -> 0 bytes .../index/ALabel.hpp.81F9065828AA88EC.idx | Bin 1438 -> 0 bytes .../index/AModule.cpp.8C79E6793B0ABDDC.idx | Bin 3206 -> 0 bytes .../index/AModule.hpp.D8753B04060BBA88.idx | Bin 1730 -> 0 bytes .../index/IModule.hpp.E1245342A901572D.idx | Bin 482 -> 0 bytes .../index/backlight.cpp.8F986B9BECE3CA9B.idx | Bin 5394 -> 0 bytes .../index/backlight.hpp.0EF1448C3968172E.idx | Bin 2104 -> 0 bytes .../clangd/index/bar.cpp.499C72A404C3B6E0.idx | Bin 11056 -> 0 bytes .../clangd/index/bar.hpp.548C657FFF81C379.idx | Bin 3490 -> 0 bytes .../index/battery.cpp.C1194A004F38A6C5.idx | Bin 5698 -> 0 bytes .../index/battery.hpp.A5CF2192CCD687A2.idx | Bin 1704 -> 0 bytes .../clangd/index/clara.hpp.37039B002C851AB7.idx | Bin 30604 -> 0 bytes .../index/client.cpp.142786E5739C6086.idx | Bin 3324 -> 0 bytes .../index/client.cpp.90DC0642EC0F3846.idx | Bin 7640 -> 0 bytes .../index/client.hpp.B4D0F538B28EFA9A.idx | Bin 1852 -> 0 bytes .../index/client.hpp.F7B62724EF944420.idx | Bin 2630 -> 0 bytes .../clangd/index/clock.cpp.622BF041106573C2.idx | Bin 4824 -> 0 bytes .../clangd/index/clock.hpp.6C252B8E1A974E1B.idx | Bin 1528 -> 0 bytes .../index/command.hpp.11D8330C13D77545.idx | Bin 2266 -> 0 bytes .../index/common.cpp.8896DC31B9E7EE81.idx | Bin 2080 -> 0 bytes .../index/common.cpp.FA1CA1F6B4D16C67.idx | Bin 1866 -> 0 bytes .../clangd/index/cpu.hpp.A6F11840DCF8A513.idx | Bin 1026 -> 0 bytes .../index/custom.cpp.EA9CE9AF81C1E5D4.idx | Bin 4732 -> 0 bytes .../index/custom.hpp.36F2F4DF290D9AA1.idx | Bin 1720 -> 0 bytes .../clangd/index/disk.cpp.1F7580C201D672A2.idx | Bin 1998 -> 0 bytes .../clangd/index/disk.hpp.E57994872D8447F8.idx | Bin 728 -> 0 bytes .../index/factory.cpp.CEDE1C1627112064.idx | Bin 2394 -> 0 bytes .../index/factory.hpp.2AEC37A00908E370.idx | Bin 904 -> 0 bytes .../index/format.hpp.A472BFB350A64B30.idx | Bin 964 -> 0 bytes .../clangd/index/host.cpp.0C1CF26FC798A71A.idx | Bin 4080 -> 0 bytes .../clangd/index/host.hpp.D6E6FF9FFAF571DF.idx | Bin 1978 -> 0 bytes .../idle_inhibitor.cpp.3D718BD05B870FA9.idx | Bin 2398 -> 0 bytes .../idle_inhibitor.hpp.A4AFAA5C5DDDE471.idx | Bin 898 -> 0 bytes .../clangd/index/ipc.hpp.804BDBBDF260032D.idx | Bin 1498 -> 0 bytes .../clangd/index/item.cpp.BDF1AC58410539C1.idx | Bin 7280 -> 0 bytes .../clangd/index/item.hpp.CF2C10DA19A462FB.idx | Bin 3008 -> 0 bytes .../clangd/index/json.hpp.6C08A0DAD19BC4D8.idx | Bin 750 -> 0 bytes .../index/language.cpp.7CFC0E2AB711785B.idx | Bin 2412 -> 0 bytes .../index/language.hpp.045E99AD59170347.idx | Bin 954 -> 0 bytes .../clangd/index/linux.cpp.AA11E43948BF7636.idx | Bin 1320 -> 0 bytes .../clangd/index/linux.cpp.EBDA54C079A9D2C4.idx | Bin 1528 -> 0 bytes .../clangd/index/main.cpp.9D6EE073CE3F67E9.idx | Bin 2136 -> 0 bytes .../index/memory.hpp.220BFCF008454788.idx | Bin 740 -> 0 bytes .../clangd/index/mode.cpp.DEC43BA6A32D0056.idx | Bin 2090 -> 0 bytes .../clangd/index/mode.hpp.6A926FBEE534F2A9.idx | Bin 888 -> 0 bytes .../clangd/index/mpd.cpp.7FCBEF52ABE61287.idx | Bin 6492 -> 0 bytes .../clangd/index/mpd.hpp.F92558038735ED47.idx | Bin 2124 -> 0 bytes .../index/network.cpp.109ECEBB28F3CA1E.idx | Bin 12826 -> 0 bytes .../index/network.hpp.959179E628BFA829.idx | Bin 3296 -> 0 bytes .../index/pulseaudio.cpp.33560C8DDD3A5AD3.idx | Bin 5832 -> 0 bytes .../index/pulseaudio.hpp.C6D74738A7A6B198.idx | Bin 2202 -> 0 bytes .../sleeper_thread.hpp.B273FAC75439EB17.idx | Bin 1672 -> 0 bytes .../clangd/index/sndio.cpp.1174277772D16F52.idx | Bin 3834 -> 0 bytes .../clangd/index/sndio.hpp.2C5698C31C5CA7B4.idx | Bin 1304 -> 0 bytes .../clangd/index/state.cpp.E5F8B9AFD9E7ED3F.idx | Bin 7084 -> 0 bytes .../clangd/index/state.hpp.210A97315D520642.idx | Bin 7032 -> 0 bytes .../index/state.inl.hpp.07C5BE693644AFA7.idx | Bin 1336 -> 0 bytes .../clangd/index/tags.cpp.901229A9EA5F0AD7.idx | Bin 3004 -> 0 bytes .../clangd/index/tags.hpp.38EBC6B49867D962.idx | Bin 1076 -> 0 bytes .../index/taskbar.cpp.3B871EDA6D279756.idx | Bin 12886 -> 0 bytes .../index/taskbar.hpp.3F1105A3E0CB852D.idx | Bin 5366 -> 0 bytes .../index/temperature.cpp.BB5C0ED5BD80EEFE.idx | Bin 2538 -> 0 bytes .../index/temperature.hpp.58F6012FF8EB97C4.idx | Bin 818 -> 0 bytes .../clangd/index/tray.cpp.02A8D890AD31BCCD.idx | Bin 2114 -> 0 bytes .../clangd/index/tray.hpp.469C1AF36DEA64C2.idx | Bin 988 -> 0 bytes .../index/watcher.cpp.793479CFFB135BF1.idx | Bin 4334 -> 0 bytes .../index/watcher.hpp.F4849F54352399E5.idx | Bin 2232 -> 0 bytes .../index/window.cpp.8AB8664D31F55915.idx | Bin 3582 -> 0 bytes .../index/window.hpp.E69A5ADAD0A0E8F9.idx | Bin 1456 -> 0 bytes .../index/workspaces.cpp.EC0CB7DF82BEDBA3.idx | Bin 7454 -> 0 bytes .../index/workspaces.hpp.0FDE443B68162423.idx | Bin 2044 -> 0 bytes 71 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .cache/clangd/index/ALabel.cpp.09E9F4748A3CBF6C.idx delete mode 100644 .cache/clangd/index/ALabel.hpp.81F9065828AA88EC.idx delete mode 100644 .cache/clangd/index/AModule.cpp.8C79E6793B0ABDDC.idx delete mode 100644 .cache/clangd/index/AModule.hpp.D8753B04060BBA88.idx delete mode 100644 .cache/clangd/index/IModule.hpp.E1245342A901572D.idx delete mode 100644 .cache/clangd/index/backlight.cpp.8F986B9BECE3CA9B.idx delete mode 100644 .cache/clangd/index/backlight.hpp.0EF1448C3968172E.idx delete mode 100644 .cache/clangd/index/bar.cpp.499C72A404C3B6E0.idx delete mode 100644 .cache/clangd/index/bar.hpp.548C657FFF81C379.idx delete mode 100644 .cache/clangd/index/battery.cpp.C1194A004F38A6C5.idx delete mode 100644 .cache/clangd/index/battery.hpp.A5CF2192CCD687A2.idx delete mode 100644 .cache/clangd/index/clara.hpp.37039B002C851AB7.idx delete mode 100644 .cache/clangd/index/client.cpp.142786E5739C6086.idx delete mode 100644 .cache/clangd/index/client.cpp.90DC0642EC0F3846.idx delete mode 100644 .cache/clangd/index/client.hpp.B4D0F538B28EFA9A.idx delete mode 100644 .cache/clangd/index/client.hpp.F7B62724EF944420.idx delete mode 100644 .cache/clangd/index/clock.cpp.622BF041106573C2.idx delete mode 100644 .cache/clangd/index/clock.hpp.6C252B8E1A974E1B.idx delete mode 100644 .cache/clangd/index/command.hpp.11D8330C13D77545.idx delete mode 100644 .cache/clangd/index/common.cpp.8896DC31B9E7EE81.idx delete mode 100644 .cache/clangd/index/common.cpp.FA1CA1F6B4D16C67.idx delete mode 100644 .cache/clangd/index/cpu.hpp.A6F11840DCF8A513.idx delete mode 100644 .cache/clangd/index/custom.cpp.EA9CE9AF81C1E5D4.idx delete mode 100644 .cache/clangd/index/custom.hpp.36F2F4DF290D9AA1.idx delete mode 100644 .cache/clangd/index/disk.cpp.1F7580C201D672A2.idx delete mode 100644 .cache/clangd/index/disk.hpp.E57994872D8447F8.idx delete mode 100644 .cache/clangd/index/factory.cpp.CEDE1C1627112064.idx delete mode 100644 .cache/clangd/index/factory.hpp.2AEC37A00908E370.idx delete mode 100644 .cache/clangd/index/format.hpp.A472BFB350A64B30.idx delete mode 100644 .cache/clangd/index/host.cpp.0C1CF26FC798A71A.idx delete mode 100644 .cache/clangd/index/host.hpp.D6E6FF9FFAF571DF.idx delete mode 100644 .cache/clangd/index/idle_inhibitor.cpp.3D718BD05B870FA9.idx delete mode 100644 .cache/clangd/index/idle_inhibitor.hpp.A4AFAA5C5DDDE471.idx delete mode 100644 .cache/clangd/index/ipc.hpp.804BDBBDF260032D.idx delete mode 100644 .cache/clangd/index/item.cpp.BDF1AC58410539C1.idx delete mode 100644 .cache/clangd/index/item.hpp.CF2C10DA19A462FB.idx delete mode 100644 .cache/clangd/index/json.hpp.6C08A0DAD19BC4D8.idx delete mode 100644 .cache/clangd/index/language.cpp.7CFC0E2AB711785B.idx delete mode 100644 .cache/clangd/index/language.hpp.045E99AD59170347.idx delete mode 100644 .cache/clangd/index/linux.cpp.AA11E43948BF7636.idx delete mode 100644 .cache/clangd/index/linux.cpp.EBDA54C079A9D2C4.idx delete mode 100644 .cache/clangd/index/main.cpp.9D6EE073CE3F67E9.idx delete mode 100644 .cache/clangd/index/memory.hpp.220BFCF008454788.idx delete mode 100644 .cache/clangd/index/mode.cpp.DEC43BA6A32D0056.idx delete mode 100644 .cache/clangd/index/mode.hpp.6A926FBEE534F2A9.idx delete mode 100644 .cache/clangd/index/mpd.cpp.7FCBEF52ABE61287.idx delete mode 100644 .cache/clangd/index/mpd.hpp.F92558038735ED47.idx delete mode 100644 .cache/clangd/index/network.cpp.109ECEBB28F3CA1E.idx delete mode 100644 .cache/clangd/index/network.hpp.959179E628BFA829.idx delete mode 100644 .cache/clangd/index/pulseaudio.cpp.33560C8DDD3A5AD3.idx delete mode 100644 .cache/clangd/index/pulseaudio.hpp.C6D74738A7A6B198.idx delete mode 100644 .cache/clangd/index/sleeper_thread.hpp.B273FAC75439EB17.idx delete mode 100644 .cache/clangd/index/sndio.cpp.1174277772D16F52.idx delete mode 100644 .cache/clangd/index/sndio.hpp.2C5698C31C5CA7B4.idx delete mode 100644 .cache/clangd/index/state.cpp.E5F8B9AFD9E7ED3F.idx delete mode 100644 .cache/clangd/index/state.hpp.210A97315D520642.idx delete mode 100644 .cache/clangd/index/state.inl.hpp.07C5BE693644AFA7.idx delete mode 100644 .cache/clangd/index/tags.cpp.901229A9EA5F0AD7.idx delete mode 100644 .cache/clangd/index/tags.hpp.38EBC6B49867D962.idx delete mode 100644 .cache/clangd/index/taskbar.cpp.3B871EDA6D279756.idx delete mode 100644 .cache/clangd/index/taskbar.hpp.3F1105A3E0CB852D.idx delete mode 100644 .cache/clangd/index/temperature.cpp.BB5C0ED5BD80EEFE.idx delete mode 100644 .cache/clangd/index/temperature.hpp.58F6012FF8EB97C4.idx delete mode 100644 .cache/clangd/index/tray.cpp.02A8D890AD31BCCD.idx delete mode 100644 .cache/clangd/index/tray.hpp.469C1AF36DEA64C2.idx delete mode 100644 .cache/clangd/index/watcher.cpp.793479CFFB135BF1.idx delete mode 100644 .cache/clangd/index/watcher.hpp.F4849F54352399E5.idx delete mode 100644 .cache/clangd/index/window.cpp.8AB8664D31F55915.idx delete mode 100644 .cache/clangd/index/window.hpp.E69A5ADAD0A0E8F9.idx delete mode 100644 .cache/clangd/index/workspaces.cpp.EC0CB7DF82BEDBA3.idx delete mode 100644 .cache/clangd/index/workspaces.hpp.0FDE443B68162423.idx diff --git a/.cache/clangd/index/ALabel.cpp.09E9F4748A3CBF6C.idx b/.cache/clangd/index/ALabel.cpp.09E9F4748A3CBF6C.idx deleted file mode 100644 index ce9ae3c77ff53668450d36c7f728fc9387c484cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3224 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 diff --git a/.cache/clangd/index/ALabel.hpp.81F9065828AA88EC.idx b/.cache/clangd/index/ALabel.hpp.81F9065828AA88EC.idx deleted file mode 100644 index 317b3e5b9a87c3e2caa4c3994b6785c740ecce84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1438 zcmYk6e^3-v0LR~3-f_3L?{436cYED+KOzh=usE+M^%CLm2I9vVn8^<*PfjKVWZ*1M zb275ysFAZWHKA#YF&TBxrpAd8b%-F+GBGq9oB$gY&4{3}C`%ib*@yebciw!yv-^I0 zc3ys#$FoZTfV(U!Smj^88UX+_0D!>y+Lt;20M9`Hf*&@%lgoD{tLkruEuN>xuAR&9 zHU&#Ao*rB{_~@!b6F*mPc;Va!zm6zMN9urYOZR&#ALvcbJm|R9^5>U6`#}9%B^aAm zcO=J14o@XHUav7EWF;RhKUQ_O#akM`-|l*O_?WMtsAI6KqwtNMzI`eE-v%o;ZE9{y zP`JiE`(|k2xW{>Z^tQgoef6i2vqBu(m^Xjb@%)^w_RPzXl7^afvH6M0>yDxKN9~UP z_Ii(BD?bUF2VUs~fsIwZ76^cgSC%GM3h7kY~tzxVcKsCKt0Dx&M_xV5YiFPd$ zB>-f!8k3};eOlbsPnT>Tze5mBLXL86|AS>xe|0iYhl^vJT6c}L^3mEY> zJ{}`}DW8iGv5|O;NG4e(%Ytf2lI#YmB_{>!(i2sF5>aEs&E+1$h+E8MV8j)0g&6TX zZ@`Fm@(VB`JTYKI7LZho^h&)3Bcsx&!N|;;4a@Ul60X1NQb3HzJmQq~LRQi$+k~uS zvC6elw|HmK*AOF}Qio-iP%SwOPj%l|{_J0GLX6CIbG&R4GLpd^AHC{)yR$O{G2#rI zS@sB7$zQu?XYlZn(TflvK8<&G|1kPXYxtB4Vq`EFEV9%nwImZuYr{yxX$09OWF?ET zJjhDYE*D<9@9&}HyAUH*WUZjqU%8qrfJhU2QA*>z@Io&k`pPgZsfhe{|ACz8?) zVyK1!Q>1dB+5Ou91^2s!!2`ukF9r5Vct%%qD1`#o_kA~GqaHp*L0;fvm$x#ti2`4C z{chE=&{Yb;QGQifo&6>Sk<@*WTTd0YP_VZxb+Q2lvMJ~(3Y9i_emp=ye_+KC+x#0g z3NE;X;9%E=Jrqot61UCivUvlwYXXa=uM5S@oiY6=XNS?VL)GjMJ+iH}J^V#@BZ^k4 G6ySesca^#T diff --git a/.cache/clangd/index/AModule.cpp.8C79E6793B0ABDDC.idx b/.cache/clangd/index/AModule.cpp.8C79E6793B0ABDDC.idx deleted file mode 100644 index c782cda4988b99322fe60e0a6a72ab6213ae373f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3206 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 diff --git a/.cache/clangd/index/AModule.hpp.D8753B04060BBA88.idx b/.cache/clangd/index/AModule.hpp.D8753B04060BBA88.idx deleted file mode 100644 index 89805e901843f756a5e62f3548a9bbe530f5a76b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1730 zcmYM!YfKbJ00;1y;c@KV?Cjnhx7=~d9dgG*h`|IjB~hp#D5BO<)V5YzP%ux@Xuuana6H^ zxB0m_Ih~;ZWUa~FR90MG7z_Xq005qHcc~EqARz!Y?Wz8J&MTGErS11x|Gv=J|6Tlh zIRjq?pY5>JEGk{woPYCR_{QYP4{Y+dH*QPmVAsUgm%C*9s;b@Lnfd)`Yi_mjk7Dj* zHu##7Ze-MF*Y~=n4Ud}No%-hE#-yPf$C|u3=X+DG)YklIJ+S2Zks1G57V7<<-2AO4 zcqH1~Q@QIZo{zuUzq+O5+>W%E8O!Tl{-MgeabVTf)+B%GW|>d<8`w?eVTs#!`>^owH3EJMy=02PTicInNiuf@92V-Z8fFMV+G0C#W!bm z?J5b%@5o;=){~Gty6k9NFs?ISc7AlV;g^T|UeA2XQsQ{M*H(0`z$hWJ^8fv!#b}$>uRR~er{?8*2+}KnxJ<5i8d~pe&-7LH?Kl|%Le>3s zvVn%yw~TFmHOc$ct`|6>i#Y{ zfrhrer>rmQ!N{*WB^1uqTLg<$>u(noxeP-tA3?kVNUSL~o<^X*amQcO);%C0D_d_B ztbykre&d#9VsQF%9fCGM^r3oz2H;-!fCLUtEYcrM^X?=lLIK2Pwnfs=PI#Q|3u(Vo zg(M`i6C9!=aDxAI?3O9*)e9q#o%JFw>b3R$-j~MuCM!0IK&QTeNLitw_3q*lkNW>+ zk|9q8Gq{kaV3||MQ=g0_gp_0WgxIuwf7f3SlPF!3$WTI_Vq^v(PwmZof9;Iw;*`Y@ z6NlnRWCGQL>~hR&T=vgp#ZpZWCPJbx9TOo#$i~Db*h~eBjq&$uXN5paoI0n#XsQbn zT;DX-U8&T8U1qi+j-BQ2FC>h>0o8 zWUOo2lJeP^Gusg+f-KlDQHWw1QzZ+923FI)HD1S(TtC!s`0&!0H&d-c5kIUPN(EByXze7tlnoj>D!PSe-(YWu5;r!_Ph zwDon}PAs3E^8DE=sn?PY5x<@$Z2oE_;l&*!fkX)O>J!p<}5w4iGP}|EIKmj zj}q(mNjVE@WY64WICa8Fo}suhH)# z!YpQF1X~ERhmi?v{&Q}5%|$;;<}>mzb18DE!)#&V0-Mgm%)!ke#88x)lPJi*0Cxk! l!^qnbKkpg*XDBX8E*1xQ2kJ`@V19l-OX=&(BW;W<3;<04iB|vs diff --git a/.cache/clangd/index/backlight.cpp.8F986B9BECE3CA9B.idx b/.cache/clangd/index/backlight.cpp.8F986B9BECE3CA9B.idx deleted file mode 100644 index 99750d6c12bb86b1c93034b38e3bd765f6f71bb4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5394 zcmYLNd036<8~&cJuT!1g(^=}A(_Sa-EA5*~o27(Gd$cM&rA=f~DO+PHMuaIu4Qh-s zwqj^7nlVPUvHU`k43ZezVDP)XGdlX?e&6fdz1wr{=Xvhygij6*=87pQAZ>DSPWt?0 zhN37QMNx(G3+DDKQB*0HqKfO+&YY2Z&s}g~TI@{E;UBCuca+52iTHm0t6fd2{Eq!p z_iH|B&wEn8A+MSLzBkujQ#Gk&qw4d;eQvLdy$7^agI4A2_5XTJW_f7|8V}_^OHEF@3nA6R^hi<3m_}Z3nF5LZPo65&s!|pCm$gP!Md%do#KH0``P$_n-zwrHf zr}o=HN`(z8#>IP{xZPHIeQPnVWbVyrhdd7LzVq*(^QpJ@f7^C_#izvy|K7Ap?kSp8 zIpc2eu8Pz*y-WUa`u(|K<$AYzKj~5AJgwHs`8rGqU~IzT$(%4MBtN^IIRM9(%RxmD}S57vK3D-z|Lm z{87lBHsj~Jd(v6R&TU-=(d>x0om3?zBMt<)lef3M9J=f}&6uTnjc8k~I1wv9^ zQmOp7en2OFLvQbc$rFsbvwU;+f0KQ+z50jNncW6ip&{1}HJP=3)p@q`?w0blIb%{i z-)(N-sF!XHG5Ov9J%7ai`?$Ro-tHGO5~BK?G~>_5E(;eeHEZ&V|L$+PP-`$;r%F3> z<8Wa0?DblUGmPEu);5nV+S*wcKhhdigM<+uQtx>L;sWC%gLBgtU0r$C^9|=# z*h&P?ebK0clVwSw+)Pom3vz%Dm&K{Vw##A7No@rY zi?|i`O|gc7@0Sx%#DTddPtxylU1I(cYgQ?lFPkvrVl#3+Ca9vla{Qanh`R31ukD?T z0v`D09WP5bbmh0}{rjH^%I568ZEYibaVXNZW`nPAL(F8^r@$>216vxRI;Ip9n$4_C zPK|>|i{po*Z)DePao$)zyT3ncWeTTY>WjbC##;5J`-VPimF4;KwrrX>=FhtQE0gYl{v@=$Kdv`7~c32Z~}#0!2|e>Wv%RYQ*>d;{~)p zAO{7nFF3z+fcH2+Krc+D&`!mUF9!whaA{=e;CJViOX&)TrBIXBRFZ?@AKU`WC)F;V zutSB8p(rFAq9q3fKfYj@{m0;+ZVBk(SPDfVk**vR{Pg8NSGX*R-TU7KHPkh9ZaLm(Rh$1Dd82P4G^Yn&J4FTUFM1BaePQD~`RsV4`; z4(sN;uQPE=ixS%R5`=_)izY{O=1JDt3}lt1@yw%6k4fR@#UbnE>eY?PJ-|VxuY6SGccnW1IGQJ!X>kqGrIb2e6+DAaoFQ;(2{dD&%iBuv} zoPYStlZ$zM8D}JPbUB3~)*-H0T8x%J!5`~yx#bWDfBq*lp-8AJ2gUwpoU(b&7iL@( z(2L3`40j0k$dXE>U-tjKT$t!~*R)+kr<76H1)*IKon>QSW2rcy=h50`!{oW!36)Aw z=&kInE(gU4_dW_6$U@mT4xL0%XscqYBL@Zl;P-$m6%*Gb0=h7kLS2ronj94Tkm2b| z|I||*gqlN9=&J0hAqNFN{OtLr6JF7WICKI2nINt5XyuXgty#GkgOG?g7g^V9yM+v>dhg z`7BNF8PLx_Nbot(&q1gbc+8CyWJ`fyv9#DwLsOvVr@ia}L$C!@TR_5&<}Kh%@F1ui z1S!D|pgTao4;|@<2yS=^1pfy5Z_p7eb;~~0w>*JQFhm%lOEAnN%!c4}@pMCi;gWD8 zf|24#LxNF~C?kR~!Wdm5Pi2DJLAV`s+0kG-m=kOSNh25$>;h>Q7_;MqE|@~F2P8f0 z=R+VF0wYmzR`j_U7kn6kjwX(l1l>*CEeZOW_*oKc1HCqoY40xCp5H4Cq!|pG5f;VZ z9`N4-5&G-Bw8IVZ5`le;M+ZzW_$DQAVP~1662X;VxDv(?tOLV3c5DLOCNMJ8S-;Pt z+Ek4un8u`W2yO+=R!}w4sPOn&^?biFK}Wu$K0$ZByFNibzMnq9V1BSZ!4zSNp|R2< zYNxibB~9=;NUyWO>;O6RZJ;8gL|73r@A*>io~cucTGBJAt6YP2%k` zR(pxw?5=CTs>h`TJY6!323}q7sG+NzuL&y{#3 z1|7MMJO_4qaiQU4Z75v!q zRoj72uoJ|cpg(n6KsP+rd;^FdHkAkV82XFiB5{JT>oEDivbXf<;0TFUJP7$_NEe@EfF8@Bm(of z4y0SI%ex$q(F2TJ3Zt!6iQsHzwwM)+YAxt+KX=)jOCm*BQ&adKFG?~G<( zFoT~VDEX`b_1A;HX{Ut;$`}vT`d5zvDKyWR1uZ8GUZ+fEq zk2JCRv=Uhvvnm%2gYlZCO}BFwKkB6!Tn;+R!I;6FAl?c38@81=t?KGp4g_N+$As19 zy?!5bt+0b;FwQW}h`~7TxbdS#1Gt}E#wOm$1tUF|?#Z)^w-U73EaTO&V-u(~f!3(; z0IuA1jQR|qKYNUN&+31?AG&J+gB9Rh0dDnW3BM)1xM~Oli^diCG>FetTNL#K0>KfW zN5G&VS|#8jep3Q$uQ7QBzS}MyZSQ^%6(G+SwN|#Ow1QbHxUl&`cY(%`5!wev}1Pqse<#*v~wUUXC ztCbk60znlB8QcuQ%^+g18Avnm_UyGD{%WUa9WdAik~Yv8^-92%8>Is;?ye3HHhiVT z;7TxC304f&fmt0pHi4`O%znHwZ&Ol>Z-!hg&K=-3YL;lO+$phd_1+J`qaKOo$_*01 zK@bdr{?Xu##XtD{b%VoTtT@(~!C3QHM+V~}snF|1)*S}mB=f<@*GHHYQ8R&N6_t84WmApU8)7jH9pR>h`2 zPa-#{s^)JWZ&8siQ6oxN-~D9e-VW$!nW(Rt;l{#&*B^aj!v0zLfp03`5Nb zPMV2FN*CHefoFuO1hg42zs+Y$_yh1(H~G>)_-7$u9c zV|%|z7ub)6;aqD$e7?%DZu-AkMDRG!$3b<}_|a_j)7k?@+|TasyjgY6U*F!p0t62L za{$PN521}Z9$_JJW8}F9e0r2UEYDG?3)ya}xkyZ~B%maS?WX!`z_4fSQ5R;GaX-!W zF=-3yMGk`YL6G&lUHV7z$%FwQn9ts5cLH&T>0~U3){@h%+1{HP9EtU z^2rVV?92Mq@AP~d1WSORm@DS@YgDZ7{`JGRz~B*(90Bct#;xi0Q#LpQ!Fd|<#t-Vh zyXX+oprb}GNi|78FiAJbgkaM6q#%OHs>uR^%Yna~wR2SDP>0)jh1eJ5xV zYzB>HkPMDheyPHjuI3O-R7=z(n5mYjNf6agldWRBwd@wdpfb!l%9o(_5;PxlWlVfE z@;|m@XRBtLJsQ8v<0$33nIUKm?Ws-EkX;)Z}gZTvVPD z{mvxg{|RV*|#teLJD~ zvGMA#Y`LGXZU?6iQ@1~ye=Y8;kl-xsS$YJMB9fvA&K1oS6U?^BwkMb)%+Vq^&wpMJ z!F=<4D}seKh4$<#6V}3Pg4H0dW@~K?MAtwZn|uki?B`Fx;VJ8%M!<3eY}iUU~8{v1*wI|P735d5y=1MeVOKendW_& z=6;!GE2e<9dYAhT)bIaVH7hqSzaTqj9+jnFshls<)SR^JXo{jDXU>F)I=XtnUO{66 zto>{zI2+sg+qsW3b2XQ_d0JSFF*Gps(Ki|I5oqLXZ|Upg=;EL~O)_~(NcbeNvL>z0 b(;zCUN?Z=3rp2Ew6$yl4rJ)g1XHfqKwtCPz diff --git a/.cache/clangd/index/backlight.hpp.0EF1448C3968172E.idx b/.cache/clangd/index/backlight.hpp.0EF1448C3968172E.idx deleted file mode 100644 index 70c3018bca32f25fb5a88850f9d31a0ed8d7205c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2104 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 diff --git a/.cache/clangd/index/battery.cpp.C1194A004F38A6C5.idx b/.cache/clangd/index/battery.cpp.C1194A004F38A6C5.idx deleted file mode 100644 index 72b2fed76eb57fa05079941546c3c5f580a3af54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5698 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 diff --git a/.cache/clangd/index/battery.hpp.A5CF2192CCD687A2.idx b/.cache/clangd/index/battery.hpp.A5CF2192CCD687A2.idx deleted file mode 100644 index b182c02d446b2968a4420d5a0d44d8587f8498a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1704 zcmYM#eM}Q)90%~{d0N}&j;GfO7LEefLJPf8O3QR+Q$t)t1|n_>OHdgTgsBWnpg09H zbx>b6;|m&JFbe9(5@c~h9b+g4!N{Bunal)4hs-Gwm&k~lvdy{h;B&n{zWF8JJa%uDo@Gz%NAD!NoQr$(_eXURhZ~5Ye@EHvpEuZ#XI36c z9na1hth$@jGJBfy!hxuR!XVcP6Z$0>PzrIfFyRr6}TbHAT-tywSlL&y>E4y}i-6LZHQb_>Ip|X_*z!QHg0ANmc z&*Mmyd{v8J1i+$3)X@OkWDhY+-1fHl#Hjau0fk5ayF{0jhH{3al)EQ#F z*=&|+D1AyKe<<^8VY7gY62NxRZlj^}3o4#``f;PWSwNu@z;4lPrlIu39gFsM?Y+NE zg#sfc_l%9ak+2hzZd&l)n1* zvGO$HX%0q^tv4YPr}SqkgZ};Mp4M(aQ&}JFiJqtQZLN7nh@8}CK+{-n^Vr@{`i{b< zf^oWF4?z~zhoLZ)(*LkMU`hU3c}zf4B!FYZSO*QIA6yO}Hq?AwZ$KhjAL)sltMm^p zI9{J4L5VL+M|aqIw_wt{!zV5n3UkFGo0URgB zIcX^COA9{qMk4?MMz4pS%nO+h36`}oAF(elX?^Ra*C4@h+BlMM;MWvY-J@a^!Df?L zB3RbTK?FNA4xSN*Jf+POv2S{EHeuuJRG<@Y$_e#&mqsR(vsw~=5E_819HzzIeM zS3L&KAgELym%Gjv+_= zGPV#>u1qQ9sm>o8OP7~u&v!wB#Sk%q8HF6R$w)$uQe*-lPvtRikf+d?FUV6@j1%N2 zBc=!PlWIx}igG8Oa&PHM@A`=WcLt~Dq%V+rxd6R^CptmP0dDd=FmVw7sP~Ts{}|vO S1^>wV$BD1ct3%5M0R9Iv?4*+b diff --git a/.cache/clangd/index/clara.hpp.37039B002C851AB7.idx b/.cache/clangd/index/clara.hpp.37039B002C851AB7.idx deleted file mode 100644 index 01026915693de5844b933966ad2224897c16e282..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30604 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≺Fw=5qR{hMu6Ozhjy^0Pg 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 diff --git a/.cache/clangd/index/client.cpp.142786E5739C6086.idx b/.cache/clangd/index/client.cpp.142786E5739C6086.idx deleted file mode 100644 index 37c6e6aab07f00cb8525460edeb8bcda4f102967..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3324 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 diff --git a/.cache/clangd/index/client.hpp.B4D0F538B28EFA9A.idx b/.cache/clangd/index/client.hpp.B4D0F538B28EFA9A.idx deleted file mode 100644 index 9e866fa1f82266d01faebb4b3d3b8691c78a24a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1852 zcmYL~eM}Q)9LJyM=~LP}9?pZd2Q60$J^C_Q>_!AL1i`l|Or}VTLyZaoiUBbN1=KCf z%*~gni<^=Va5EWmz97qVT68*G6kWtQGIdT_!X`{*ZqBzkb;`n{&-MQJ=9hl+ySqO5 zd~!2#{Qglo049{=)K!+&6kz~B1OTAACQ$w|1OR9NsB3PSkvC0~Ahu*(>uLQbIC+z< z${DvZ(b%>2Bki}@S0D6M)m~03-_(}oT%Gam!}xn;eU9?F(O>5E7Q5eXK0QxgbZwn| z$>hTI+nO#Vg-0K34?7i;_h8DJ6-%N$_gv|rN?tT zEy2f2ADx+y;XP7%SZvB&Q50O8{#(qw8p8ppcYfm5*v-99{2A8s3l{labEUZkBeu`i zr_SxzcOt)N)`HHvY2V!4vq8Ie&7FUHH;{_p!3yiDDSv4OXqu1mSA5ya^U{x|D*H3!J+7ToxuVgH z9k;g}>v@=^_VK4Hm-Q8vZxc~8>s^w|qW02kRqIq(6;GUl^#Blri z{Kmy!eOx6VjRLSo^4Msoci4MA5Wl}+;|USPuqVhmSy21-<+IlEUo06Ek(KpUoi+3i z4;6g3(o->}M?gpcI7vz}(@^)%{?c=J;6hucfZ`JYHi;%94Yfa~Tx`#ZNN6x2Q2|)7 zD=9S8{!-PeuXLVzFi$|HM1Unxil(9VS2y1Gtz6#G5Qd@@fDN3%L__U=O+Gl3{q^*x z0;%E!%rg(iCRpDQkU;_1D|uxa>i(aD z1!I=Y*>zVyaw5P+(P*Th_JPu}>KOWi&ne9TRNCMnbR&n+U=32sxTym*Vm;Zy-l~v2HD%SbOBH6iBdF=OxS< z$WdEHWuD|O*UvT%KUz{`<_zMgD+DX4N){6Za#RrJ1>~p?OasVK{0q2A$0|$3%{qc5 zP8vb5{F3Y=*eYA?1lwcn2?RSL90r1&PN$b(SEg$M69sZqkdhxf-k!O^2uN_UKH0=H zfjrfNSphj}1Cszk3O{3B!&BA?JpqCvHIZQqcF0r68QYMfOf!@rM-gV^LY@*E^lz)% zpMCwZ24i`QoQbjBXCIBRYnm&cF${UiE<+ab6jeqjk0jV)w~Qp% z#koubyE(UsaS3_KBf|{x6fZ^<_|cWgZ;xwyyNlz)NrX<%X~RGmD*@1kMlLjx(8z~IeQ4B$#{U6D C(7TiX diff --git a/.cache/clangd/index/client.hpp.F7B62724EF944420.idx b/.cache/clangd/index/client.hpp.F7B62724EF944420.idx deleted file mode 100644 index d21bff2b44e5b6be0b5a967634aae27e649d72f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2630 zcmYk;dstKF9S89DJpq0>;msi=F$p9A5|SJUkn0j6Lq#bf^M-iE%UWb~ErogsDt6X( zqh1OYYw>E=<6GZ@`Q!V%$@lra z=OpKx&y$j(sZ%E^5X%2qQEhcaO<5E|NQn>%*Q|cL6eDDnAynJ?LdogMK9A}`@=wkV zEP2wtX&^s&^pm7bRfn(HkJ!40Y}d~(eCOpYmS38py8NkUDoYprc#6At?;na^d~d~1 zJ@eZh+U)yZeWlXg)tFv&YWYC%j=o(b9eh?tv08k64mPx3s_VFRdFA!+wHd?P-plTPS=_zkUo~r1eRca-x~<{% z-gBn5tW`B_TM{cK%-9(>($)0WQwhtaP5N-hcfOZ?b9!n^ZnL8>XTzHPo+lSz+rTxA^E*afe_*k4fiZ}3`~wenLm7hfB-Qe{)Zj|R{d@6mY!hqb~R^aJ80rH zFdNWG``a|!GWyn@#* z^}p7h-1BAkiO?==R9(ndwTw(9?U#mGS+aXiPOq6ywa_dCYo=5V3 zuHxcZ?=(6yq`rGw{KAz>^7>*>20MZhl-381@NT*29M^ZdU*H50f!?9_(vWuOId@@i z_uF>8ic_;A*o?NZyY!O}^ubkSRbhd%hzLT~kduZqf8jBG^Jzl zW8b;Cy5n*cCu4o8KXtM+|Ihq4K3dt=nj>&_5kb%z^w5y{$o45hmD_Yt#o5?=nlUX^ z>aW+E*J%rvjH);t>wSVRMe0Z2cxiC0eMLmgjbpt_aExn5Q%`k+6bFl(8)S#({^HZo~wF+F%Ov2=mmTpuV?ct-VZ-3Fx(Y zV?n5MTIVE#4im`MW+yY8u!5q*EW$iBh|z-;l$^PI!SU(}Rim7M#b8MyV9Bs}30R6O zvk6#>tg{K&lr{|k+ay~70nsOh2&B2w0`q36#j87Vqh$olU{(^a`EBC~IKio8dSRZb z#jwH(iqs=BTDLxv_x?gmz`z?cj4G_4M1_}~eY1XkNk~Q@flts9$aH0nC*b;#YZ5~X z^AxK!u>Gqk)#n9Fz?@{ZFtRXDsbWIO6jUYx8eS7es6mV-te|h1L|8!uV(?&|g44A9 z7e}HA*xmMA0*>j9MFi6MbR7Yw-+R9HcUYU$W8z3iLl4`c+~E_W^gkIR!wz#H)9GqW&HtztxBp3=lb;&>_%fkZyh zba1F{ed)#4Mohp07IMh*ebV9ObA~wrKEE%AfIr~RWl&+B0>xayJoSh%#PO6L0w&&M zWAb1H6^B8Cc?wMLrEhOEHU052CJ=&9EMo>MC@)ME%u`JmBABOmFf*`%TEU3GJf-21 zCwdg(^bPl}sm5$(KwzH2Ff4CBlQQtTZcM<)8Q{eO4&UomWf5E)zYV@vtiQZcrKHFxldPZb9yfSgyv5@ihfKj}Ju A$N&HU diff --git a/.cache/clangd/index/clock.cpp.622BF041106573C2.idx b/.cache/clangd/index/clock.cpp.622BF041106573C2.idx deleted file mode 100644 index f3d91abde97b6acdf0c9b6eff3dc3fde49e659cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4824 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

C?1ntOB?_mA)VX1=q(-F@b> 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 diff --git a/.cache/clangd/index/common.cpp.FA1CA1F6B4D16C67.idx b/.cache/clangd/index/common.cpp.FA1CA1F6B4D16C67.idx deleted file mode 100644 index f4bdf8fc398755fd1fb842b4c60cf088e201eae7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1866 zcmX|=dr(v77RApw`3NC=NjT;t0f7Vvd5{1xA<9GENCX5?6!Hlw4+WJ<5u*2k6~WR6 z;1!Tp1!>iaBKYD`Kq!JJSdpm#Yqhp;v4|G%)jN1a9ns#2op%4RX4dR~_WrF|G2uF0 zusMc>Y!1)KNZpwXFbrd17-ra+y^Tr3uu*di%c;+cP0nmyL_KVDebf3fCE|9^g;eR@ zRhJL=0-Dd%n;)oCzaY8GuZQ2fIDZs2erZuLW_b40ug5QxRS-5}PdfZY&YVwhV_B;T z_1ps|4Vx2;^JnS*nixBB$at~z$d>$aKU622O^h5pm;BMMz6r*=q4xP{u;xh1;`+z~ z2QzAoPk`Y`Ws=W@yywny_Wjw>_q&pN?@SETcaG1V4j$gUxX0(ef~e=8Zn$!8lj6ljjVB&1URB!Qm(T#Tt`}Z#YF6PV3u9bguoDG<{6m)@hr|h!0_)6np(Z0U$ z&aSTgSvB0Om*1ROzNv}XHTmyM$jgLN}kka7mXjaYmkU~=Iu9@OC6O?PK6Oh z^(S8D>mq{mnWt;r`(E=EGj7xJ39p)ges8kH?SS#J)tbxIN$rDIA308jRxx{zljP6d zwq~Ixo8v8lXY(Sy{Q|JIE*qZM)n?|B$0?~!cdw|v|BMG79u1rBZY`==`smRmcZQCZ zx^Li8Q+c6&S3qA&OgXdeK{Nd9<*aUt?Uh3x$Nm06;Z=p)-`|+k)KZ@lSwXBF-I`eP zwxTlg_e;TmNc*-g;}7tJmvJ!_{PUzx%+Tp)8B=naa`qlze*E z?wFXc$+_o`V=H9-aW~r&V#gYvjG)<4Iphvp-OD~p?^(|lxSE;f2|a<{eR z=ZKPyH7?xoaG(2*nO?J~e;m5WXqeFHETa2LOD^`oW!{2?jKwWObK#};@aVf6X|n}u z>V{6-+9PJvByxi^8;i;dic0o+De!48zu}eVl1W z7HF)9bqN?^3YpG&wmF-HF-+CLFl_I@k&e=@qc8J`xC9IlHiSqI3CQ?+pPia!2%}*b zEp52%m0L4OK%@a0Hbh#WWt$mp=1scaSZxjvOU!Zxh~$$3fG9p?Qdi0iAS##&0f@)v z2>{}$c^b=Ot2+F`K420MNtsd-BAHMoVV@}6F5S38NrT8nWut+}*2mTlB0G)U;srVB z`k~cEe;Pz0TahC~a*13CkxHUca=8t$E^qZ|I7AwbhAXr=c-Fhr*#d{i44BcC`r7!T zC+ydotht$`x-X-{dVI@k21JfBM+HPqGA9K@VwqS0k+aNM0g*zj&_bkCE42{0s$I1Z zxvAZ>UXKoXK7OI8#UZjJEZHl5;T=EoS=Y-)S!j2k*Z))ued3`|AbeG=M~Wkqtm{2e}v^m4iy$sjJR8 z7dUm72@s7(vj>PKrpWG0Iy2e)vDgg^*KVw0k`vs#4& zM1TMm5YYkM@|Gd)yihYEq(MZGgaE#$0TGkWw7b1z-P!i^Src1Nrl-}|#D^O0wEtBT zTL@VHxqI(bN^}1m93lxr!XKZC7HzcQb}}Gh*|NkCap)X2L?oN!LqxGDlR8tb5K%r< zFho2yj}H;gg{OWJL?q6QqErGRIY%ynNX1c!vQxJwn|f!+-eeG97>1+xqUlqKK4b52 zJ*NM&lcw2}v3a}6u8obwm)k6~)h!KO6ru=J25B8!gWbH;k|k0{Pam0!SZMF;Z|CIa yy-eimE)Vd~ELK@Z^TQ)n#Dww8xP&E(4b2%eCLLH%oK=DaR^-a~$Td-`vHt_G*^UPQ diff --git a/.cache/clangd/index/cpu.hpp.A6F11840DCF8A513.idx b/.cache/clangd/index/cpu.hpp.A6F11840DCF8A513.idx deleted file mode 100644 index 3528a582327d9f675f562e78f75385668ba4f881..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1026 zcmYL{e@IhN6vywo*X`{+@7&%t_ugKsrBcc)BU6ji!k<}$nf3#x2;~e5&Dx|CMJzM= zVMZyE`J+O!Oc`kwh8Yryr4mES@;U)8Q&N(UaMc&bF18jCCzlS8ZE%`vm0X9itnV zp0k$B#K#k>(rp1#^;NAib%ikl*HtNPvn$8^e>Qy|u54XYnv`pp?s^w%%x-@XTpiM$ zIThti-nuiF@w?M>FQ_%-==q+qU+uGhlXp)$uT1Fvz{c{yP-}7iUVj3Bu!kFUs`z&{ zEhIER2*f}}0%R^A008sP+0hTq5W5n}MPF;whPnMyRV}CQN}3CNpi%=6Z{)*;OtmDoW| zs^#tu_CvCOldHN9D4~z&vqsi4!JE=KXn6GGr4M8@0O_^*#X@q||LC6|>ASJZ%tBuc zKq{z0LUQ|-oPE|v7pdq$+EHxMcwxlpI0HtU9~X!buj36E@qT=uh?1ZLgt2oGr5|c0 zVjxCZsHHJt<*aWSmC&D`qpXG)F$}|E#Ohi99m%S|sS8mOh!IC}a*TMAmz&3eSBz)& z$_b40GQF?32Z9p#i`WTTz+KZ^vgXXdKqY|@vz!SQp%atkmar)cFYJ?F>vKS*y z&2bp5eA(eEb!;*2B>>DO9Qm7R=aSa3|;1{VAlt5dAOwa;cktqQe zSXy%O_PDOeYRR@%xC9oCONjr6<_hV;iz~c6lJrQ*BWaJ6c_eb!AjjFd!f9_Tk&-fs MhF(D8@u5fl1E}f+LI3~& diff --git a/.cache/clangd/index/custom.cpp.EA9CE9AF81C1E5D4.idx b/.cache/clangd/index/custom.cpp.EA9CE9AF81C1E5D4.idx deleted file mode 100644 index 17765069753c33553cfbe832fa938bd6e49f04f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4732 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 diff --git a/.cache/clangd/index/disk.cpp.1F7580C201D672A2.idx b/.cache/clangd/index/disk.cpp.1F7580C201D672A2.idx deleted file mode 100644 index b5c499e9b9b886a69e4abecc68594fe3720035ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1998 zcmZuxc~DdL7QQ#!ge3efBw-B@g3$;BLlS~WA|wPt1cHIcl4GzgK(NIPf#*O`Tyc9m zs#Tw$XssZ-MQMu)Vnsl$SX>aFRY2(zaUV-9F6j6syy^Svoj=Z;nKNgZ@B7Y~7o*c% zw8gN@Dm(TJe7GMyTvr>W25(g5=_%3=aH}opEjR@L&^jT&&REucdv_QP}UESmxQT@&|=GcU+dt z_uO};K1)pZv*$iJ#c(&qNruUxjV0%FBj%Z|KgX^<#oUyWQ2jc2zP5q)XWLI^qH=e0 z_-~7nE_XDftlu_l)J*@}TpQ@7yzFsk6(gN#Zf_EEp4F}`Z0kC9A?r6rj6Qon*CZZK zG@NYvDDWA!q&9UWu4xGWKCklBhwdS>?n*-Iuqe`VM`fou?co_8U+R0|;l8;8PlDoO znTocLV_zm`bY2p*u4&jg@v7|W#`dad@kQ@M@;7v*IeHa`k*AU z`EM!9shY86g6Lj7+i^pk+hN;lRp#2BUsG!Kx#s(JKY4TD1M`t!>)5BpjA`4-9<{Bk zu(%J5Z3*}4Ti|^mRN7!1P>5A^lSa*~9yDDzV>V0*F!CI?#w@_+dpOJ;7r4TjldUi;y#xSmZx zAR_aol8j^_(2`mff+#YIg+NE@SP15jb65zX$!OMJaN*2Jtn_!CD;u|_{DLD8IfxwN zuCwmPM8t2$5y-veK8914k$pkW{V52LGvXmo2~?taYtOAz-|FIQ2Xn0hLBYY5J}E=?TRnHV6nd;fy9nBVfC*-ScJ$s85r> z&Y~b7J;U+jxI01Af-LA+L>8bk{&$a?ik<$@q2o1C7rk_ja*rqKu z2m+)5AqXUq5<}! z*4Bw$e0h|!i$Z|hN$yI3##6)pF7~LZCry(|ML?s`>=2Ly$w0t&;&Txwxk@ht(cEaS z)4F~6--VCfu_b`&O!XjuI)yr&m@FY6Kp7B5fMl*DfdJ`jsfvKGDPap*{~cbpuJfxF zE6WVIWBaGjz~{09zvBq_?tK20hMd&Zwr!Uv|K$#WfnuOv6_#EbJ@~K>M-WGeWA-|+ z2X7P|^}`Wx7#!A(wUIYpgpAhNA_(9I_#lwTBnkvFnM{Eo$UevgL9i$|5J7}}gbM<# zNE_H6JpcHQSz}o^0t3^)>W^O&^*iyb!MX$DI}Qx540L>~>b431(aFI(_ur<~cizSk zgwjHt?!QF!tEy$?|A(OG8p@*adtkv4XzVl|SdL}+66+Vp%}LL7wFaiir(iW4Jo@yp zjP!`RxCu*Nk+IyGN=i<~=WyLUbhD$Rk)m*aO_2AD2yuu^7(9K-ETxa%R4?8%m8T#~ xq2-4L_^RcBGbL{EE-|tCdC|^PM~WS3k8J5SOd7#(a!7FHu-FE3T;g0K_8+q*x$FP{ diff --git a/.cache/clangd/index/disk.hpp.E57994872D8447F8.idx b/.cache/clangd/index/disk.hpp.E57994872D8447F8.idx deleted file mode 100644 index 4ebdd74e448bb6a72fe77db95e33b9a2a370144e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 728 zcmWIYbaT7F#K7R3;#rZKT9U}Zz`(%Ez`#&kQk1!qfq~%^BLhRloT(H2xepoe9Q~cT zN3Zz!U6KBKwtY92t+}!5(vmK5``uspgA*UL$TS4MtH>AWb07wrxP8OJ#j z+o#*A6}Z3MtQe77=)dn(@*L;(gDL(z%XXZhHnyy6V@ioAsDa@5)*wrQ^MkfAR`mBewZ0KMoyeD%up&alKkJ@Ry%$a=Dg!v(u|T z9XGc<{{{DLLfz8-#(8t8X%qzqz!NV-fF3bz_95cjIJYbbP%zVsz>?s$t zbuPy2;AP}t7UB>RojkAFxa+`yRz@CXUUgm_n4cLr!G48#k&z4RwNtB$q6^t~A7|uY z<`&_Wf_Z?E3GDEq)SN_+KZ}c!i-SQ7=H-8E>ta^Pm9cTKb272AFt9>`0R%V^OlX9G N06RjI1HoiMFaZ~|x9$J{ diff --git a/.cache/clangd/index/factory.cpp.CEDE1C1627112064.idx b/.cache/clangd/index/factory.cpp.CEDE1C1627112064.idx deleted file mode 100644 index 554a2f5a49579a1559668c1acc204b5b9722a024..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2394 zcmX|>X;jl^7RLW?c$Gk2LjDF4!V(}61B4}zKmq|0mQW2#6cV|B+7ekrV8p})TaP@4;I8x6dl?*IHz;J-E+S@=ef`CNt=+Em}W;& z@wpSqO7iC9V2Yv~D2lSqu@nw5D5~C;qRMtGHs;JaBy;Mn5BT`~JM+v-EvNFvEt+`H zt(-Z!wC!Dm(}6DV^?S7a(nCcb*}k{+QVUs;bMVP zRl&d88+QyP*=*Uw@ z_Eo#AEz?ge|K-8pBhb4R9LBg#Rl`g$A`^(8anYXSMF_z5ET9ngyy!YOZrS4sQ zWi!NkB=nN}uDHZ);aR^s~NLJGH=^wR5&Zj){4- ztWq#GZ&Bcj=CyI1jw{YRyV{-?aCc}^uS;i3U;H~|Pwy9tJ%6np`(@Jy+HS|C?T5EJ z@qK8eruRds$W+mn`(%%od*-rP4~7rrI9~H!aB;!9P5+tO+du61Q$W5cF1K`@m3Rlm zew*IjKX@nE^O1$M@eQv>uS)TmJwG*}IH}qyyKF4=DVt%r9i4whZ@DmE;k{c`P<_DP zXp+W`Z@x9*&-Hb5N>9X@)JCuRhDj&WZtA{IdvJ7lox4%6*u=ZN;IrYr2G;W1<1@Vm zg8hGcWIBBA;;T9@zCvmekpAv(g2MkEGV18V&e30+j4drSPt$Uu2R7t7tBUu`_Zv)4 zZEhJBNxJXuo3^^(<>Y10e#tubiofu*EUoGvVHtuaT6gz(-ibRGu94T(J{>jnN=@p= zy?fVH-@nz6k{`=3{1z@vrQ7wAIL(8*%@`i8Y}idsV~JsvExWHaE3?_wro-?} z91M29g#_Z2U$4-+m^S!y?aEG&`xc$Nws3Xh?n>fiG%Y^kccstmm*MSitloYP*ZUd# z6HnyB+|+y5U%VEFxwyY2qq~+=eKltsWi2l;YblD-IPb1L6>w!0hmo000T$ta3^hyb zOfg4Lp(v^@t}WU5?6%iD#^h`Y7&OBrBa#tm_y4;kFW*`)ilV3^N0(0OFO?z!IUzY= zcB{m*^qX$>BLWhUB$%^8S={=siYbVIPNb94OVpKZ+q7y#ATKU2aZK6FjO&xyVi*MS zobp@=%y63F${PrXbLLgdMg$BZgO8x9xO01aUne4<7wH4s;_0q~*>VFS!13h-xX+(@ zpy|%MX9xq2=J7q*%OX}YZ#=dkknfo9PN2}S(49b$Ly@cJYjl%4`*1iS!1Lt=Db8vH zV-lVmLl|VVjIUmnvg(iW4hg~_q=o#bw)S3c8{cb)087O3`^zh2|H&v#;Ql+@;EYTg z0t_a@k$iH802W}c#F~9mcI`Mch%hK=C4c;#!>%RoJF;ys(|83<0Jm)5n~&ax-&cMK>a# z6e-7o9w1|6;An_JIb&guL^j0gxN!k|LW+O@a0@wPb=U`%2F0ag$zi~uKu zqawhK<|Yu}Mewu)1WG{+0bzttOF*m=YY0dpC9wpgfl>tlnZGQAfFeL4C!mxnLkXzE z)zJjBLE10^I+;#MKp&!45is~0LI@ZGjB)}H3K0a%QReurkxq_hH1kw=#)AY2E9rjfx!@B2*qHO8xd^igx7E<7zl7>a_5f-hxNOxu?D^Fc&FCX)HwOer~4GqCCj!l0(r{0{5n z24Uz6AtE3ZNqz6cU;I2u&wh^xaDBOq4LEu@7+Nd6tsf>`%sVN)j;c1L_q8( z4(z|KvVRPx# diff --git a/.cache/clangd/index/factory.hpp.2AEC37A00908E370.idx b/.cache/clangd/index/factory.hpp.2AEC37A00908E370.idx deleted file mode 100644 index 330bbbae66fa651d2529daf66b2f3090d4257bb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 904 zcmWIYbaQK9W?*nm@vO*AElFfyU|`^7U|=XNDa!oLz`&r$%D_-DXYLfoydw%cu9g=! zoM+tide`X_%zls93=RcMxy?U0Z0(h+7TgmY|9|Y&y}74xbqrr|yOH$q%!Q?9+^lof zY?XH3^2USf2ERqyv7n-bm0Ohe$;V{W5&D)Z?Rv8lC;+> zQdN!JFxh3BSitmPD;YC~DCsp7oO12i3Co}CjCXd|R&91JEz~~N!>wVnreIFjy}79ZeT+P;){55ZFsCtcft^&!-v7oa z_c|XF537}cl`s#h9fut^%z2DlU}sI9S8d#N;6N)#jhdA<534<&y)ev)jGSOw9!B1l z_<7IZKgbp)Yfgru)SN_6SQQr~7Yj2oFffQg!VUyD5KKt~lN-U5KrmTw(|L3U0aUT$H2HUT~%7A^?}K7?9m^nidkLR1976h$yW=^YA$VGITab_7!Z c!4yI;xe!bq1XB>fltM5$5ll7&lOMqZ02w^cI{*Lx diff --git a/.cache/clangd/index/format.hpp.A472BFB350A64B30.idx b/.cache/clangd/index/format.hpp.A472BFB350A64B30.idx deleted file mode 100644 index c466d85fd1d2e778f6ae4d6593714c58bc24e7dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 964 zcmYMyS!fec6vpv0_l%Ry&FN%mlZLuCjkH7)TR{^{ja6HWoy4UsC@Quwm1o|@71Gf>^p zboKXvuUTF3K({^J9=clC_f7o*+ifg3f1*_JM$~XRcDQIyPoi$I3NyFPTf-(4uX9{P3VH_zTMaks1Knav-nI5TDB4Q0hAMQb~`&fjur zGEc2+tQ{u-%zH5R(Pt%IsIhD_Ah&Xd2xRqsJ^(C^)OYOra%io_NCC8)?G};DzVXQS z+CxvLb!yC_0Os%NnH~`9uY!w0Z@Bd3Ey}EmJ?}*K#D-@R1M?#y~mMfAuA(^O4?U4ZN z&5LKI$%H1G$;kx|VX%L&fH3Ng78ydwg$!Xn}Ad+ z%?%He-|CJ_gh6+(*sx+;7!i8NV>5sxE?^7~Bs3`|&V>e{-{UXdnYVvYi$5J>GU_FI zsf_v zMHnlI1upxl50t*#GFFns{~CT07j6cZ%movP^9gMZ+dx8ZzPHfOVq7TEU#n!Ip{DmX br;_nhUhfN@-Vz`1wtZx&B*VX(-uwIkZn)q# diff --git a/.cache/clangd/index/host.cpp.0C1CF26FC798A71A.idx b/.cache/clangd/index/host.cpp.0C1CF26FC798A71A.idx deleted file mode 100644 index f29f9b635674a94d8aff6d76ac7ae5b91a940abe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4080 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 diff --git a/.cache/clangd/index/host.hpp.D6E6FF9FFAF571DF.idx b/.cache/clangd/index/host.hpp.D6E6FF9FFAF571DF.idx deleted file mode 100644 index 3d38ae58573fb8663b45953337bb66f04bcd3460..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1978 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 diff --git a/.cache/clangd/index/idle_inhibitor.hpp.A4AFAA5C5DDDE471.idx b/.cache/clangd/index/idle_inhibitor.hpp.A4AFAA5C5DDDE471.idx deleted file mode 100644 index 632db4796066a727ccd52fb0ef17a1933b1754ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 898 zcmWIYbaSg>W?*nm@vO*AElFfyU|`^7U|=XNDau^Tz`#(#$iPrBr+30`uEPdAu3ufR zzFV8TAbH{9z$8_%AFc9J^!|REF)7GrdXAITcdu{*$>YHrjJ$g#(@sf+6|NDu=yqO0 zTyy3J$Mts3oZZ=NAFCG6ynA$6N!z=xktKzP7S3B*YjU>G@ulLHnqZz+VRb)VX|FS^ ze#Lzt=*6n)J7&iXq%tge6gw{lXk86kpz1vJ;ZIA)mCS*!_8b4%!uuuI6$6aU|`^ESI+mEcJ@6x6C)o38z&p5 zFqmNAgvc|pF$Qjs$x(B=e?fqWgCC-YO^97c7;ZvxeCXy(mR&PNnRsCG{HpxgaQXB+ z<*iRE!cL1aalz#IRQYt^^0Vr0Yh8RG*}=!e&CkFlrXZ#QCgAe(=iYy?{@?#;0!+Lx z{k-hFym0xuFQ)&>I2TkU&LjYn=V9VuhReU0lJv$osytGRi4i8x4YCRr0w1UU_L2X; zDV&dqlb?Z2R6$f9Ou*%fQqzhRkV2A?3mk&5fMezY2N)07P#!iOW*z~~HSc#Msaigsd`vuS+(z7{JZvHyBHWYbRU3C5IM50*!G+rs7M_e;;BbTmA0sC? z*!DdA^>Om#qt6(5*!cMPgift4iY{c|eVmbpjZ>CW85Wj|jNmYQ7l4=;C4lTLP6s zGX(?*7)fkEENer;DIh3hrp9bRDiEW{DBi*x_QD@jc0QjcF)k)% z$N^ZtJFXx%D?h^kz!iW!KW|Sg1GaIXpdl-9bNN7^bv|LT{o1MOcOnvQ)f_mIk|WOE zd+yPnYMRq3XRj1r?0jO~TtML9g^Y=cva*Fw7F?8WTCio1cQKI;0H1-m4LO@>UiWgmWb@$|OttmwDTU9Fls=Q=jMMU1|boZ7hI zubh)_e%rERz42&kVteJ-%l6u-@}z@@x@)F>`!xM#!~T}A_QsG-$K@SfxpOm~1x2M3 zUzZ&Eu$dSAu@9+73}2>g>@R=3qhl<;YZ((4(K|QL89#fprg-^6+Reh=b@syC%y|wt z#?>ZOSJ$UYxB$wHyLl5}G5|il-b^u5=Ex&GLzyiS98iwj(UqWEn61{B;&6uu2_v8a zcz~He3r1&HYQy7u>R(tR;Rz@+H%o%<43Fb$hOhrQ{*;6xpcGf`1l<`AegAQE8E&^p zcmT?kyIKgkGlH}0>V2MmxL?8xP)2T)1l{7X{n4{M*2q{11E6f&=0VWCCH#KLY;8l@ zTN0}P<;VSO1l=O~R!_7g?XwsOCqVgge=9+^h)=eZ2iT(GBm|(mxwj`lw@C8)V)}Y= zsuUUf846!O%l$4a^Wskf^KnQj6{ zI=;Fhaz?@pP#)aFOrV7yd1+djKAq~nu|~oIC@Z&0f^N~Zw*g~|-GvfnK$*D7LePCj z&(?_O*Z}7zB^&_d!+p#I-FIBe+4}eSt3hFMsqWLVS61nJZqGC}S%VN8&yUjLTt=TAH{&V=Gd?ykvVf+T5X qm>?sX1}5nGwL53x!CmKNy_T^e%$~Q)?!5A#UN1H@cFx~F2>b^!D}z%2 diff --git a/.cache/clangd/index/item.cpp.BDF1AC58410539C1.idx b/.cache/clangd/index/item.cpp.BDF1AC58410539C1.idx deleted file mode 100644 index cc3ededd68a1ae68fd692b777a7332dfadf756e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7280 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 diff --git a/.cache/clangd/index/language.hpp.045E99AD59170347.idx b/.cache/clangd/index/language.hpp.045E99AD59170347.idx deleted file mode 100644 index e5629625db90e05ef7f02f0be509e690ffbabb2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 954 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 Y=oF?F!QgY33bn~ezCnxqmn;MS0wE?=lK=n! diff --git a/.cache/clangd/index/main.cpp.9D6EE073CE3F67E9.idx b/.cache/clangd/index/main.cpp.9D6EE073CE3F67E9.idx deleted file mode 100644 index 986d6c37ddc99c10c7655b0e407765630e8d5417..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2136 zcmYL}cTm&W7RT=|MH6yE8W@s52q0CY1PF#2l#)b<0mMrUH3%pRf}&WSB6d^|WcR6v zyF3Yki;5r}1uO1zD0W3fMP!wgeOOpQ7k9#&k^9GYJ~Q8$JLjD5y(dKG@2?~Qup~*g zAzi&To&W%#0syktW~GYo0PH0Ju%Yb0^7xEnZhFI2fu-dWJ9IXB?v_ z`4Ky*Q}(L?k8{WT?@zsb=libKS}-a$^>j>(x3GEO)pn0~Os9OPe`4w}n7Hz}U#2Ng zzigR)zjE%w61DUCuWI(-4R2;fd(P$g8#6u5=6BR>{d1~bP*?GgfA(AFSklFFRpq0f z*U&z6sLq9NJanhPY%nKZ)!uOYhFzKa2SN0uFoV`Y#Y1BM+lI;CRz2_h>;6sW@$GLO zX8YrJ`ceicV{AU5IPzzV? z2LDamyZa}COs430k$!Qmx%lKK|B-@((qQE^S}@>*(IgWv03knMZj3bvzyi% zwp61R`=6!s6{pu0Ojh)=^Ou`84!7o|?9I7sclFdo_8x51@NBoJ?c&&aIawU^Wpd!I&E8HL^R|fF#b-Z!b(tyd{t(0ifGBe)%ozbd`^^9VdpJvf zcxI^&s2~;qL}rlnXJppzwZ0&=D(!+m1px9R_>pGhyI=TsNcXPnq;?1r06=y`yLmJ6 z-5+eK-ko;moF5g$0f0yp5^F}j`&sJb>^wXGxZ_Uu2#?}QFo?Kxu0HOJGBE93=`9Q* zBfgQDP8iM0=GX!<29biL;8Dao-skb|F2q4Z!_g=Z32{OSeIRsqv*>6c9-;u#01Jp@ zma+v9DXo_Fm8Zv`!47U|KMbEt+IL$-S0?K_oB`81a@k z@R*s>IT%Dt9VUY}b-t}2!h1aiQ4BjqU{svoyT0{bLJT4jk)*>fDr)~N+Lne9kdfHP z-6WwfI<6+X7Y~u1jh+x9v7a~yB8jgg&~nvjdZLGY5(bf!E@dxxm#lI8{03<|vQjuW zfBtFMgRAQD3{10o5OFd&M8Q4EMwP{r`e4ei=Y zzn_9ZWX3Y%_>cS{jx4Txq#c^j$VFy+W7pP^7h^bxjOQ8iAhM)e>W7m3I?~njgBV0A zYnA=-!)I!}S~IFJh!7j`R`l#SVQ&<51|!U*q^hrLuluBq#0W^h7YO36;MPduO*I%q zd^(@4wkpcJy<}xCMwqEN?TP8_$rF{?7-6QPRd_-9U~^k2o`CoUd=3Heg?uLhG8P-V zrMKkjhV97-!yqzb8L~4pH7_Da(JL^BJaL|s?OdlC!+`p1ZI0Xk0YoxmnK?vC6Q$+8 zLQ!LIxAO=F5m|>!EATD|h~<90f)QrkY>|K3}5_6DIJwAC&>|wjr+LDN5a@Toaec&hK6L^Sd zEE*Rg4u!*nNXQa$d-0pe%*;k797IyGlm^i<$}+~l&fy0ahluBK5V;ZDND%oDd`Qnt z11){8@)lzRq;gaVUwUNb@dsEpFo;}9uI$(2EzdWm=h$geu;4Eo?Y`vk-nZ2cgNVoE zu_vFlSAD39PsKq*#Zh%2a=5P v4?8PwC!veI!LoTW`O*-nK9Pl^P<7#45?*HxfviVUvYB*-A~7&DC>Z<$#?bcP diff --git a/.cache/clangd/index/memory.hpp.220BFCF008454788.idx b/.cache/clangd/index/memory.hpp.220BFCF008454788.idx deleted file mode 100644 index a16e087593e14b38a14b55d1c49be96500af30d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 740 zcmWIYbaT7I#K7R3;#rZKT9U}Zz`(%Ez`#&kQk1!cfq|ixk%6INPVa<`e1{cyTz<}6 zXYuyLwR30h7#1}NdI(Hl42}Ht*41;#s;qX8xSai)Yt*Z_CWIz@5K_x@E`g>swJy^c6<^jWcgiD~_fmnA#5HWjPu zZ(^S8efY58mU9zL4PPog_9jGSOEO`cb6+;!kU zDc~nzL7RKKTFUd=&Y!I^Xl8^+15CRew0|+7_iXai~kdjT5rCShf1Qk%A)j>8z zVcdw=?E(s@XgiJzV-y>-m1&SxQCtv(VNjtNZBQ8$ofGD0{Z&=B&Z+PI?sxCv(2x*0 zjv#ZALbKD8vzB8Bf-n#S$;?{2Qfz@B2#z4x)p`23HGNucyH5BvdA;7pHEVrCNvnm! zu^ry_PMbBuWdp4gmvF*|wq#=HLCVd*l9&=!Pv(dz;7ULQfz4X?J|IyHY$Dkh`*lW^BFnr|B2_?Af8VHD#lHWu46#T-P^v z(VOoCN3IsGSoZ9U-kzUTw%#rx(7*~SFV&u<1!hgZmZ zH~qIsp>MzU;OatuNt#|O*}25CW#=^)@utry&#;%WX>&Yo1{TDQPEK0nRFrMgh>3yf zp!unb_7Gz|etvhUHBW<7CbGZk^5d0#AG|6`)W;trL|m+ldfi=f*Pnm(aBbzrh^mIh zrADtkr&4PLCs+h$+7o8tPT4_A$s+c31N`x{m7 z;v!6@&vxHB9%zi;!S8#zW$*Q-2XFRe4+-k+uSY)u8TRGOq2k{MuPDeb;`(it4PLmg z&#|_!x+g!+1ah7y+V(Vc-I#o{f6ppYl7lXPqWDA2*-uIBWhD<^jApsQJY8B5 z|JubF*vl+_yV+}<|yl0{V4;ksL@U4>e0p!EA& zVLeSJXOwa#JtI$6?r0Mpie+?c}VE2rE^1Zm~l8y~gvC~;WF`+~IM&!1pn~sH2Ra)88 zhdWw4@JF~7#wCZ}W$S|Er;%I$OG>IM!8KqaPR z0_cXhF*yZ(+dGVbek1^ya^^Gu6?O`HtL#)mTVzK73ZTkXC9}4!(JLnnDJXz$EH`W0 zIh;rJF&;WHfMkS>fhkD<;u_o=KrWd}1JIG|NE27C2;R_?uR#Ii33(EUu;P?Yxjl^p zAcw@k0F+@eid=s%?@;8p4~jv7STId7W^m2e9k>Ps5a;5y4%1CPMak<8Bn*m}A{GV- zCc*kb3#Ct?QWiEI$=vosE{Z|5M6FO6s-h3oiet^ckU2B}WmK7^%ag~gUX-HWFaW6} zDg{8Cgi`>dljsxx0R_-KRKSUhhb5o@5)wk`{kD9}D=Wn;mC0mU1E>HBPEhTQoX3}> z3s3-oCE(2)d9=94a4!u7&Swi8F1k5f2?KL;;inDJQ|n zvNhPH=nx7Zol0k~;1m!ats2BAfD{S^WY!OF8aj@eh2d1(I?F{)ex^asi<|)@ zW)i`<|6!EmcbU4)Z{)Z#pAW*w`x4AWS79nTfNF{w6jgW)q)4yzq5$%p`R-+7As2%X z;{XaE*N&_B>yH~fN}HR_lE|)P4`WN=!Q0cHwV@c~+41bFr3PR5E{`~Kt!X^#8rstH z=OuePNdPhg3>koOo?Kdc=DUhJM>FEhwH678FRn)EQ~s%|cm5;yh}A9<1_@`v9fK;K zN_Z;Dd%M4Jd;$efC>J`NUS}wzpGkd%0!XD&*%w3hWH$y5c3WVOVneaTAVo%Tz#x@F zl{EDS*5|eCJa6W2sj_K1u{I`y`j1~p|0f2ZhNQvUxwLmCc(+`rCMObS8h!A-vVJTwxdXFTgC#^CcztQW_ zc|wtGh~M1lbDRTJvpwxxg49}f2k&XJ8NQB6g+y#0K*;@lf~7NEoo0DxyxasjUg(0b i#q({*)+9>?6HsvrIt8O~S&R5qY)(XM_>zT@$o~M@2G;2S diff --git a/.cache/clangd/index/mode.hpp.6A926FBEE534F2A9.idx b/.cache/clangd/index/mode.hpp.6A926FBEE534F2A9.idx deleted file mode 100644 index cf2c74c8b009180a3859096d38aff9992ed720cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 888 zcmWIYbaN|UW?*nm@vO*AElFfyU|`^7U|=XNDau^Mz`(GFk%6IN&eDmy`3@QIxc+sm z_@LmX!E|nG7f!I<__jTw+lGzfUrGJ~0n1cR<|`|9a_Ib)Tx2eB(ar74>ixB=edqta z9+%cUFRi#TH%W<+fkE0?@Yc#`V}EugMt%l1ZZ>W%Fu}kH@)`pJBO9Z}vPv!f1NspH zOnd@hF$Ok%c79$44u}c{ojbRWn6kB>;bYK4vfhmw#FN z%7X81$1*-9ZUF{1F<~)DFaekUmVC#;L&4vhkBLiwflWkML;_5}<^P(WF8vkvUq*n5 z2WGzzyAU5-z9==VSR5&&m?0s=19l}38y_SSm-1b8MvOdcV(el(sn{rh)VmpZ z*tj*hjbK5@$OR6%?k{dToET<3W#VBIk`a>UVH0N(XPrE++PLe$fmTKyHePjJ9ayL` za)RA?YIRX`A^Yy*AOl6XrC?#o$OI0{+udLEt4en^F!HboDGKSrLX?pU9FnjAWaI(| z94x#TIU(Uxl$w(WN*BdN$;H7S1~Vj;fB-9k$&O%hAehiJ1OjXZvgUX0Ywav!<6vTD LVP$7vLMQ_OPzTnx diff --git a/.cache/clangd/index/mpd.cpp.7FCBEF52ABE61287.idx b/.cache/clangd/index/mpd.cpp.7FCBEF52ABE61287.idx deleted file mode 100644 index 7d6d83705a258ce42ef152a027165062f9050ef8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6492 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 vIz@L5GW)kn(e_U0)329=+u7aE zUhatD!-rx3UYbQmn%RNim_TH*J|Mcv!gat+W*5uY4-%MI$^f&)QM3!mKoC^tiq^YZ#jYS#3UCY`c zR+r~otP~vMyuXE2KX;^W?|r~uFy)iDzT;c{6Hd2B6n6?;OUN8Y{fX|dVtVu(=60kLbzXAN!@Mta_f}mpwU%`Ylc$Pt6~(1Rg_kq46V}$0OVRTz zLq>LI`WM{HSyfQ-U8p6*e*1oX?Kb}aZq|Dp;b;ClG>l)AQ0Qo0w{fS|xpwrW0p#%g z_1iXhTZh*8U+P#SjlLpvwl^)20Oh0CunYVBuvcTtG20 zz~)G^L<8WSn+yPmc#t$CJff;pjbI$WLWsc80NjfnV%Vo_Vcvn9j4lC5GQhEsvAt<1 zXRt3f%q{8DFJC|=8Q@5LWE2ghPuy>eZ9h7HfPf-pfQ@>ik%rQ{Jq7tY?Iq7d6f6Vm zR68{^ls@m*iTSFHsxcx8kzZUhPFJV*Qu?u#_oc}d?-YqBiuH*?VuaF9`02E6OY?;U z5%pqyl8|Il`odE!Z?1i6J}RJC8Q^G5bQle#pZ03d)!R*5z71BZRdVMSn zr7w;ze|%;@%PA4**!9~5d*JtdhtH`>m*@{gAjokyHd_5IZS z0?(&$Qr0WZBMqG*GROeC1$Q_NW#4~l&662zmCYjREd%TlT;Vj7{%Bpn&RqA_UqmFb z^E(7b;Qi{bweC3kX-uYwjI8gk?jNe`U%9hq=F&~RTLKD_0hXercp6H7{RSDOfsdDq zD2(kVtCIuoPv=3+HQ%nvCIhm{0NZ$*hK925^Uf&a{wGbvQ(*+V(p?z@yVKnn1bfmv zo{&1jAAMF741@$*cuP2w7sOL>gg!NbZ5o>(8EczdH8KYYc8X4&b@|QT4_o3_SG>e&r*8kj!FQ+55)5AXcA(Xbs7>>1?APJer5TD3Pd z8xovsNVYPHkf#I(qtO{77bZKP3hPX|s61}ikz%h{qb69d)rS#m(wf2uw)V0b2)3*3 zT7r|*Nm_y(YDZ6+9r4UA^j)lLFT(9cj-U%kYIbb-RwIMXL;6Ew;vMh&U9xpPmrTe zWd8N<2l^&_cpVa)7M5manjlXVF+7l?XsUBpoG))WaTF08Y6z7G*73St3=rffoYnP} zsVk2itAYeuZPrA_2Xd4R(*k*_gyDcZMZqjUjv8p$<(pWl{`wRoSO^o0=T^8DHeqrx zNQGVTt`rq^$GcNh*kkiJ+e0$mT5Ze!5fbdmbY(L$kfT-@5fx8q5V}t2I-%>=mWkS@ zSz;C>SmLE9Mh0?}3KIg+3mHD|oWh diff --git a/.cache/clangd/index/network.cpp.109ECEBB28F3CA1E.idx b/.cache/clangd/index/network.cpp.109ECEBB28F3CA1E.idx deleted file mode 100644 index 5f659f91839340819147bc27ba2dcb52d4f6e1b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12826 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 bVZ;m!1(`^s#>#%C8kM^5tls_mL`eP*i$DzZ diff --git a/.cache/clangd/index/network.hpp.959179E628BFA829.idx b/.cache/clangd/index/network.hpp.959179E628BFA829.idx deleted file mode 100644 index 87fa996cefd4a53e6dc1c454aac85f53cd8b0e89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3296 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 F{{mcCorVAa diff --git a/.cache/clangd/index/pulseaudio.hpp.C6D74738A7A6B198.idx b/.cache/clangd/index/pulseaudio.hpp.C6D74738A7A6B198.idx deleted file mode 100644 index 40bf5907d709a90dfc20cedeb8f98395162dbfd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2202 zcmYM0dr(wW9LLW&tK74fbC2s~*(bZQ%U+g;fUcsX_=3bonjm97@-d>w_&^j$5ucc* z0wO|+BNUpITKO7dEW*S`qxr}vQ<~CCqP57;n=x*|MeBS&?Bepy$Ufti^}92y zK5x;|oP-5q_KujBzy7bj8KDoxzsSD+?qtJqPf69~yvI93HZSaU`dVR)d1iGtbfQtKdKh#|aotl{PFgo?p(0!f>#be9n_P@Pi z>)6D1D(iDvM_g=5o|N`vWlMFsyR@P0NapPyb32aL-84UpP;jgwe zMay9nSrvdIX{3b(z+GQ70Kk}VYC!D;>u`$93c!Ih(8$8eJL~g0+xu>y$ff|?oA$P{ zP`SBi(T(!><{JWnE`WV~e03~<_SJrfp+~Ol73)ktMUlS(u#-AXEYuA#Z`wN_)Vt~^ z3QzzJqhV$iDj%|{!&ryXYACWR07uY>UMy4|zj5>PCwp$T=}-^l)t#_KZ}IX8R%e|) zB*RHjcLm@m8s%i6&L=KjSL*X3xk*NWd|sxq_w#3rwnyBxZPHO>PylvOmx+ZsKQp>x z)RVMh!4&x@00+}xKNc!a8F#O2t!{{)5e4)86j6=JQwPmRd}J?=iu$tz&dLJ1fzTc1fdFMCbRL}4=$3kS}_=DF8dD!=Hu9t2M#-m1*CB~|C?kc02kSBU zE+L7i{N=p1@XgzQx=E3&0Bkea6c#GaOr4iSGyuSBPda8bop>J-tnaOlK}8Q+%92`g zA;AGCz#x9nawT?m=K~GF@<@3+!GS1HBsdI(iM#@!$kgY?Ly?)z1&1ON+dOdfx*k<- zn?SH^kzEA)+5FrD>&^Ngg8i-j3c&`eK_S>^Gr9@3BRk<0pb?q+gk6tds-cqWt$XLr z#4o9Y1REj^v3*O%4ZJpGVFx7GiJXKt0~A>!@FIXBD}X_ZKRq20b@OjXunpOI@*035 z(?9Ot0jnBI8%7|49mpXP9FD?8UIkFgDuA00MP~ejKA~r4v~H0g!LnP9=iWmt^F3KR z@??6du1F+UGD&uVEt18LV3**M2v!6|A~;wGmI!tWZi(O!Aw(iLLWqzEjuav#f}?~e ziFXAQSx4|{fFi2|-UJY`2H@gDkqOT|ha&TvYYnwb=k*5k@a_kD-hc#~9p)%5HPkYh zxx-Lo?s8?JmZ@6$;mUsbwY3`|!BT|e;le_ZiOT(iBJ+~#2(?T@ZXeV#>$q%CWRh{W zpq4px@$<~LmkQsUhXngtd;_^zP-I4Nk)X%~`o+1wWmn^)k0HVSk^ZqiVqAIa$eF2ON9oLe<)cgG+pTiKG<;Ainx3=z4s;$F6@C|38Eu L0ML4mg7^3z5~vkq diff --git a/.cache/clangd/index/sleeper_thread.hpp.B273FAC75439EB17.idx b/.cache/clangd/index/sleeper_thread.hpp.B273FAC75439EB17.idx deleted file mode 100644 index ee3351c34d12016680e3261376da49be2729a29e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1672 zcmYL|drXs87>Cby`u6tq?H#_-f>7Hcl`>Wafx@T~#8#B6j;X`xEU3e~6sI`iye@H$ z%*%AnVVvR>bqOwt=;jbxyFw?%Qd&4ok0a{hSoCcot5ifFLWr6W60EAMxJC%M$`P`(u6E;v*=MtG^wZ>D-m2L8;ru>j#jbHl z<_nGYCt2=ZY2A0TYf+RTJ59DS<^IDjn^$$5iAw)Af87nzRrS1T-8=5FB}$+2y8Tvp za(#7MThFN%>CJ0)kGQ(BQBZE1h+9t2_3rIeI+Iie2rt$o}gJnhTfP;$?N0+O1!e z{4`Uz6VSh@_&yL*)2!WRDV=)w(Tmc?s+ZoF`Ryma`^5i`B(-tJ*kJX%0HuVaT`;aF zUG(o|fpP)?4b^BFh@yWLgb@GXLkFJi%!<@dDiFxG4_jv565iSk4ZLntr5>`J&Y<%C*J zxo`r!j5jh6<-q$m;1<8qW!6x=Kp@YOHLhdvz2A0)k#JGlcR@ z#XGH%k4hhFs9GRUWGNcT5Xv`CI@h^5Yh;U#Y6Jo{xlP9q%J(gw+_d>hVX=vt1OhoU zN5T-w8;(DJHNteec3{IuQOgj@ThGO=9#xmpq@gN-K!K&e$`H!`w0`}t{_oQZHB=%H zD6|v~Wf;tZXV0DfAor+_>I4F*@>CT=D1TaSdEUE3`5vW)uz^1Xg+U>R8zhx8W(FM+ zLbz!^cX#dB@IHs&*Z2*JKYpFuT=eb|il7PC3_%dAf>Rg1pfcmHF})PQmS9UkaK7Q3 zGGtRAXUWOsuSpQxk!~x3C(>g@D3BLmOw!h-qQvX~hv2n%<7`hFjvJosS;HYX!W>Zv zxb#xDqH7xmU_z5xRFUJEf|;^A!573)ND=Hxdl*8zGCoYyQHtPDIgAJ{mCJ~b49SR) z37N<M&}UGI4RFeL-t$9o`fJ!L5q+h z$j0!E5lE5qt`t7Qrv`t9j<8s3b?3iq-o| z(}IZ|?GzzdooqtLRA-u2t@SQG)mn2=f?$ib#UnUf&NKw4&pH0%RTn3J4S zvna3}L63SPf>~kKA~;eVUIf=jmk%LHmZU~Vm!+$DCb#G`$1|6=pIy7-lDqpn1#sKk zu>c;MC$=tTVuo!)^4k=_t#XGWcvPNnQFe|p$qzgkvuo<=gV!j6$!Ibo8dq7PP33Y?)T9&S~qRz}5v|(wOv(pA9eewwzzjA^3E@2(cJa z1bc)%8X-O+J{rM2%$=>WDA|tIawb!IACAMR1LCjYr6kWvF@9FR@mXXLUjt4Wkv6Gv-e3 r?@+L^Ea>kaz<#UahmCQMK`NEW6-pu-XjuP|4~~k#Q8_qD2gm;bpOBqk diff --git a/.cache/clangd/index/sndio.cpp.1174277772D16F52.idx b/.cache/clangd/index/sndio.cpp.1174277772D16F52.idx deleted file mode 100644 index dcbcf66a93e0bc7fd46a9b8fac102640380ab6aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3834 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 diff --git a/.cache/clangd/index/sndio.hpp.2C5698C31C5CA7B4.idx b/.cache/clangd/index/sndio.hpp.2C5698C31C5CA7B4.idx deleted file mode 100644 index a6d18d34376287f1e1ad737d131bb841036f3a23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1304 zcmYL|e{2&~0LR~Z?c4RWch~FMyX$R3Stk)9gv`ny=@@fkGoy-RPGcMd!F94>ZEV+V zIDgMeqLoodAWoED6Oez4h9;wm;nF+03gy3uKNW5&;tQz?rHDvg}y7c?7ZvU^u)eo zwXey4B(^K(fm3a}udKdSdoDCI9UmBI+|^c`;QoN`EbcSCU(YLL?e@YH_*Z=r<^!xE!gI|1j ze?ejDWA)pyO{ZrYS1q4zPIp}^-}zQw_kAUeCob-|J@>0CBfs_CHMQxH&jzG8x%hIh zvbAB)$c<;}au01=l+3(dko%~A^y$g{Z*1b9_m5)3GHIawvcfEOMe9SoDJc0OYL`m!B!PB!~zp0K4sO7Y&22+y>C-k+zhE!P_>Ne(U+Y_%8`r+4>I7;WT(`&EeSXRn@&5f-=A)gV<;Qa_{>P!`&x* zmkv(Kyo5|_1sCU1jTQE6pA|0rynjeUnF_$NA}^p}@GjqsZ!L-TOUTLQ3!ESrygNI6 zVd~9^l!)vK!0H0kL&M;`p3t4dW7EA7B5c0IN%Mbj*fI1-a{HM~6{(E#h&LJY`~A`9 ze&{=C7LiQ>SdkTthQW`|CQ4F!D;^e+pa7i3WeGG4{?$k4uO6S%azrHRC+ybpXc+wL z#JTzvr5ox*TAJJ#7a)&ii!8S)XlnAnAwT4?_~JJ@>ogOh_*{nH8?ll$;ytGBXvyt!%5ug5$L9Z|!7z{V&ZEm^pyxGE; QGk|5DaPyD<-+6uf2WM$TssI20 diff --git a/.cache/clangd/index/state.cpp.E5F8B9AFD9E7ED3F.idx b/.cache/clangd/index/state.cpp.E5F8B9AFD9E7ED3F.idx deleted file mode 100644 index ef7eb2abc55e669d9c28fd1c9419e5096bfa32a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7084 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 Hh(i7cL;DM{ diff --git a/.cache/clangd/index/tags.hpp.38EBC6B49867D962.idx b/.cache/clangd/index/tags.hpp.38EBC6B49867D962.idx deleted file mode 100644 index 17ded0c57ac63fd7e3000ca8fafd49e65b05e410..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmWIYbaT^TVPJ4h@vO*AElFfyU|`^7U|=XNDaw4$z`)?h#K2H7XKv7GKUPJar|mPc z?ri+bd}rnk6`n8W1S(li-Fba1e|n=~TyoO7JP-Nh?ccV%iPUX9tL1HW+`C#UkjHh? z#FbIFmGiSH4>WQ+$XSg+2wO+r_b5rsDyPaiX%pa#HyUO0( z@~`nrl9-(KQm_BZw>a+35|Ep}XeHlr)1}c5s+rB5YSs0enHH!$-F_-+-vaKvRp;fG z?M^%>@GGk!yI}Icl?i=?(iyq)oGZI_iJ27re`&le(x(5w1@Aszk887&+MkC8aBtJ) zZ*8@R5`UDwMEu{R8~)3d-v9lwhN0y2MJI;h%G{(RMg|5&37)e@k9>*XV-gT%;1pFB zH3Ab19H0PVU|>*FW&6ARVp1O;6O%9lr-ZbGJeYvXd+gaOthM6sBqk<)L5L1cE@>_W z22QB{&=vcv=6zOI<746zX5f@ok~RPnaQ!oG&vEl_GI=h>#3&5a&&R~a2A7|ub9$MA z)?P^gCSI7lFuSlgTz;-<;-iUy%%k7!Rw#37G#GIT?#m(~5l=85p?Md^l?}>)$g*9!?=?A!S&=Gjf3gTxqF- zX4{n~ZHzpe!t%oEuwZB80ta_6r|aI_RDnK59!@?XK8X-NA@vs%ZCDw3IHlO7cwu48 z$OR7DQuh8gPPy0ln0Pq(wD@#+I3)xmgeT9dHtsrbpcQ0-EuRxC*cmy&!3_&#Mow_( z!UC0%3mllRFl1x|huyK?{vqGqei3El;S}W*RXnd6y)aL8g$E-KrzE>17c4*-xxfJl z3qM9qaJW5;ye;wbp22@c9!?$^9%WeQF+)O)2kaOgPJU*7_J2)$n|RrkdKq~*r8%V) z{&lz*ggXCH2YFdoN|K={H7602UW$v7i-SQ7E=cMD0S*L{9l_*AFxe1HPOG-oEkQFa Q{<3j#vvaVrF|Z=!0ArILO8@`> diff --git a/.cache/clangd/index/taskbar.cpp.3B871EDA6D279756.idx b/.cache/clangd/index/taskbar.cpp.3B871EDA6D279756.idx deleted file mode 100644 index da30b7b2c157c04d7033cd7cef0ad65e3ca015fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12886 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 Md#0hVzCHT<4?Dw`DgXcg diff --git a/.cache/clangd/index/taskbar.hpp.3F1105A3E0CB852D.idx b/.cache/clangd/index/taskbar.hpp.3F1105A3E0CB852D.idx deleted file mode 100644 index f16ecfa1d5e8f9648a60563aa451485e7cfda18b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5366 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 diff --git a/.cache/clangd/index/temperature.hpp.58F6012FF8EB97C4.idx b/.cache/clangd/index/temperature.hpp.58F6012FF8EB97C4.idx deleted file mode 100644 index 82a29096ba7c8af506d7bcd13a78c5738b044c43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 818 zcmWIYbaT^UW?*nm@vO*AElFfyU|`^7U|=XNDazc-z`(Gbk%6INPVWRq?n4GVt-sUv zm|cFH_v-562ZbrIY20gdbGs@&ZjXLeV9O)(L}1eQPqs?yo-nySk@4&~7R1eP%bIDu zPZx)I%~y$oHdD?rAN<&48@bZ?#-g&i6Vrq~Fx|{Lo1>%R9nvHF+QZ_{=4C6wI#+Fa zx<%p2y8ft<;3}t58`KLb|H32m)zZ{fj%Z4W^PGtc^+nAc41za#~C@n9!|NSt#dJE2QMQJvj~TXD9qc;Tww3=fX(4y z=3(aHgZY<{3GC0w^Qw)z4jgD@*NnXuN{}b7kiT*_U|<>si>@m{=Itz=B}Fj$kq&m;j*0&!hkV diff --git a/.cache/clangd/index/tray.cpp.02A8D890AD31BCCD.idx b/.cache/clangd/index/tray.cpp.02A8D890AD31BCCD.idx deleted file mode 100644 index d907a6a2994853609beddb733ee264a4e73b4236..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2114 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 gl4L0sE4^6dit<8 diff --git a/.cache/clangd/index/watcher.cpp.793479CFFB135BF1.idx b/.cache/clangd/index/watcher.cpp.793479CFFB135BF1.idx deleted file mode 100644 index c0bc0cfddccdc26b3caab2bc6f40fc2adedf861c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4334 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 diff --git a/.cache/clangd/index/window.hpp.E69A5ADAD0A0E8F9.idx b/.cache/clangd/index/window.hpp.E69A5ADAD0A0E8F9.idx deleted file mode 100644 index 7f457c59ec1ca16ea0029b8c67d668407f6539c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1456 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 I?+q3GA9=D%2LJ#7 diff --git a/.cache/clangd/index/workspaces.hpp.0FDE443B68162423.idx b/.cache/clangd/index/workspaces.hpp.0FDE443B68162423.idx deleted file mode 100644 index e4f37356d26c43375d9da06060c2ecfbdd3eb81a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2044 zcmYL~eN2-_9LMjjNO^kK_GziLw6srK))or-ARs7$k=KAmor-LT8=~T5Ql}sjaW00v zDP)LaIHSYYIj3&EEQ(A)89GH2MY1TH3y~@6oRev)6BW03?w;Ns-~5tK?s+bk&-Zzz zWu~WlO_lBd#xPGl$__MtCNNq!>r>n+oORFCA7X9=HUwyoI^{5Gzq;BTT&zt7vk_yk@ z?hTgJ6`B3jx~`189Xr)0F0B9SkWB^Zj`+QK$H2#SuQzV3kIB&_G?&*YJN6vioGd=i z+7q*J*2eaR!D#y_|J=^Q&!&_|s}{XoKKa*{x4ie@p;@SgKft<}TB@ z!>y^`{&A~);NrN^{rc8{yvI4en{UPO2QOu2C(m?TSkUy~>3#gD^nT`X*gebjTFG+H z93%t4FW#J0^QvD3kIVv~3kS*rl_227r2_y09zM8KxbBTrTIA2TL1}PGT)XdZ`83a! zCp;k|p(KVii#1m&^y|0{n`D+!j2>?4qXFLswC*`+~ z+40WPT`J@!030fYM$?dZT5^2FD?z23cofXeZ`au!5}%NEIVLY@MH!EDjN5cJAJ6VH zTN|p67O9X)060txi=`p$pP#B59lh-GAdfU`f3PmtcRvf1E40Q-p5r_UWL(e*zWZJH zD5n3Ir#?rIAmbXOA=2|#JuBSNcJiMB9%&i3>a4!=)s8u{-__*};t^uptTUUX{pa&f z-28a|*!H2wDgdmv>*Huhyd$IYX8fkcEEO6i030rckD?**&a>0|&LsIgRUw%GutRi2 z(~x-Ank9YJV(BOz4Q1~S(S=wg{&UL7yo3p=$8rQM0IU7gJPp9T^b=y}{ZI@p{N>CR z719d;M~D&8G^8gyKR2e$z5UWq9tE*C7CP0ohfQb(|Dm?QXa@240 zXLC}Q9eqCv5*#iMC(LrlQM(<9d#iGnUr&PshXsUbnd1;qx0%vVK~-i3Lj^UL2@4fe zROTmCP%o38#J+c<_G%I&*oJHhW+&vRl}tv+Q3rHwa!G;J! z6v2XAP!cS-gy95RmDWIlZ6j@Q1l#3yCBgP^`$&RafvzBe-A1=HYqHjLdtJg18Nt?Y z>qvr~a;K7Dm)xbyE*R_G#=EK@!Rko$7$!VaP|=y+kfUBRogqgxX7)mkT3g~yop7T) zLxcod$6GT>dqas)6(VgDYPE1>2+oM#^36Oepnx`FJPsB3R(u%0jQwvUv%VdTjcmA0}>o64^^-RKm|3Q z2@g3cI`dnmpk5Pt4+LvvTIJO$uUKCC!zx6uF~T^SVABXwd~eQG!$42uzb|gvsJ31I zWZ~{LQ@@izg57erj&%VlXa}CJPK}(A^28-0*eo|I3AT*2BoXX(xreh>K<;H5iVKSJ zUwrRNiszP;y%^N=^G{cRZ&dh3xo_lsBk_$Y--sW^C5jcNBY#sURcc--58xDp{?h+n H92>xYDhu;A From ab0f2c13af984e461e05635ffe5f29453d65105a Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 20 Jan 2021 17:48:35 -0800 Subject: [PATCH 168/355] fix(client): attach styles only once Gdk >= 3.10 has only one GdkScreen. No need to reattach styles on every output change. --- src/client.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 24984a4..9fdf870 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -129,9 +129,6 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_ wl_display_roundtrip(client->wl_display); for (const auto &config : configs) { client->bars.emplace_back(std::make_unique(&output, 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) { @@ -232,6 +229,9 @@ auto waybar::Client::setupCss(const std::string &css_file) -> void { if (!css_provider_->load_from_path(css_file)) { throw std::runtime_error("Can't open style file"); } + // there's always only one screen + style_context_->add_provider_for_screen( + Gdk::Screen::get_default(), css_provider_, GTK_STYLE_PROVIDER_PRIORITY_USER); } void waybar::Client::bindInterfaces() { From 7fa1c118335cad40fdb92db49f4e35c8f8dc0bac Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 20 Jan 2021 19:36:16 -0800 Subject: [PATCH 169/355] fix(client): unsubscribe after receiving xdg_output.done event Ignore any further xdg_output events. Name and description are constant for the lifetime of wl_output in xdg-output-unstable-v1 version 2 and we don't need other properties. Fixes #990. --- src/client.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 9fdf870..0ad4e6b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -123,14 +123,14 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_ spdlog::debug("Output detection done: {} ({})", output.name, output.identifier); auto configs = client->getOutputConfigs(output); - if (configs.empty()) { - output.xdg_output.reset(); - } else { + if (!configs.empty()) { wl_display_roundtrip(client->wl_display); for (const auto &config : configs) { client->bars.emplace_back(std::make_unique(&output, config)); } } + // unsubscribe + output.xdg_output.reset(); } catch (const std::exception &e) { std::cerr << e.what() << std::endl; } From 3bcf3904849bc3cb91799d992336d1b7fe59dd1e Mon Sep 17 00:00:00 2001 From: Martin Pittermann Date: Sun, 24 Jan 2021 21:39:14 +0100 Subject: [PATCH 170/355] add power to battery formatter --- include/modules/battery.hpp | 10 +++++----- src/modules/battery.cpp | 9 +++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 08dd79d..41bc0ad 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -31,11 +31,11 @@ class Battery : public ALabel { private: static inline const fs::path data_dir_ = "/sys/class/power_supply/"; - void refreshBatteries(); - void worker(); - const std::string getAdapterStatus(uint8_t capacity) const; - const std::tuple getInfos(); - const std::string formatTimeRemaining(float hoursRemaining); + void refreshBatteries(); + void worker(); + const std::string getAdapterStatus(uint8_t capacity) const; + const std::tuple getInfos(); + const std::string formatTimeRemaining(float hoursRemaining); int global_watch; std::map batteries_; diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 86f24b2..8577469 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -135,7 +135,7 @@ void waybar::modules::Battery::refreshBatteries() { } } -const std::tuple waybar::modules::Battery::getInfos() { +const std::tuple waybar::modules::Battery::getInfos() { std::lock_guard guard(battery_list_mutex_); try { @@ -210,10 +210,10 @@ const std::tuple waybar::modules::Battery::getInfos status = "Full"; } - return {cap, time_remaining, status}; + return {cap, time_remaining, status, total_power / 1e6}; } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); - return {0, 0, "Unknown"}; + return {0, 0, "Unknown", 0}; } } @@ -248,7 +248,7 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai } auto waybar::modules::Battery::update() -> void { - auto [capacity, time_remaining, status] = getInfos(); + auto [capacity, time_remaining, status, power] = getInfos(); if (status == "Unknown") { status = getAdapterStatus(capacity); } @@ -302,6 +302,7 @@ auto waybar::modules::Battery::update() -> void { auto icons = std::vector{status + "-" + state, status, state}; label_.set_markup(fmt::format(format, fmt::arg("capacity", capacity), + fmt::arg("power", power), fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted))); } From cd97bdb30f1d6d5b6ebc9553bb9ff1e13ccefa28 Mon Sep 17 00:00:00 2001 From: Martin Pittermann Date: Sun, 24 Jan 2021 21:49:00 +0100 Subject: [PATCH 171/355] document power formatter in battery module --- man/waybar-battery.5.scd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 869df32..a4650cd 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -96,6 +96,8 @@ The *battery* module displays the current capacity and state (eg. charging) of y *{capacity}*: Capacity in percentage +*{power}*: Power in watts + *{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. From e19aa1d43af943d83eae532833b2ada0baaed31f Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Sat, 30 Jan 2021 01:41:45 +0100 Subject: [PATCH 172/355] [sway/window] Add app_id to usable fields in title --- src/modules/sway/window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index e920345..d8f113f 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -56,7 +56,8 @@ auto Window::update() -> void { bar_.window.get_style_context()->remove_class("solo"); bar_.window.get_style_context()->remove_class("empty"); } - label_.set_markup(fmt::format(format_, window_)); + label_.set_markup(fmt::format(format_, fmt::arg("title", window_), + fmt::arg("app_id", app_id_))); if (tooltipEnabled()) { label_.set_tooltip_text(window_); } From 6cc32126052c58b8fd87cd400f4270151ffcafda Mon Sep 17 00:00:00 2001 From: nullobsi Date: Sat, 30 Jan 2021 18:04:59 -0800 Subject: [PATCH 173/355] add length limits for MPD module tags --- man/waybar-mpd.5.scd | 16 ++++++++++++++++ src/modules/mpd/mpd.cpp | 34 +++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index 8c33c62..3f1ef9b 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -73,6 +73,22 @@ Addressed by *mpd* default: "MPD (disconnected)" ++ Tooltip information displayed when the MPD server can't be reached. +*artist-len*: ++ + typeof: integer ++ + Maximum length of the Artist tag. + +*album-len*: ++ + typeof: integer ++ + Maximum length of the Album tag. + +*album-artist-len*: ++ + typeof: integer ++ + Maximum length of the Album Artist tag. + +*title-len*: ++ + typeof: integer ++ + Maximum length of the Title tag. + *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 98332dc..ab82224 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -2,7 +2,7 @@ #include #include - +#include #include "modules/mpd/state.hpp" #if defined(MPD_NOINLINE) namespace waybar::modules { @@ -98,8 +98,8 @@ void waybar::modules::MPD::setLabel() { } auto format = format_; - - std::string artist, album_artist, album, title, date; + Glib::ustring artist, album_artist, album, title; + std::string date; int song_pos = 0, queue_length = 0; std::chrono::seconds elapsedTime, totalTime; @@ -144,13 +144,25 @@ void waybar::modules::MPD::setLabel() { bool singleActivated = mpd_status_get_single(status_.get()); std::string singleIcon = getOptionIcon("single", singleActivated); + auto artistLen = config_["artist-len"].isInt() ? + config_["artist-len"].asInt() : artist.size(); + + auto albumArtistLen = config_["album-artist-len"].isInt() ? + config_["album-artist-len"].asInt() : album_artist.size(); + + auto albumLen = config_["album-len"].isInt() ? + config_["album-len"].asInt() : album.size(); + + auto titleLen = config_["title-len"].isInt() ? + config_["title-len"].asInt() : title.size(); + try { 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("artist", Glib::Markup::escape_text(artist.substr(0, artistLen)).raw()), + fmt::arg("albumArtist", Glib::Markup::escape_text(album_artist.substr(0, albumArtistLen)).raw()), + fmt::arg("album", Glib::Markup::escape_text(album.substr(0, albumLen)).raw()), + fmt::arg("title", Glib::Markup::escape_text(title.substr(0, titleLen)).raw()), fmt::arg("date", Glib::Markup::escape_text(date).raw()), fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), @@ -171,10 +183,10 @@ void waybar::modules::MPD::setLabel() { : "MPD (connected)"; try { 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("artist", artist.substr(0, artistLen).raw()), + fmt::arg("albumArtist", album_artist.substr(0, albumArtistLen).raw()), + fmt::arg("album", album.substr(0, albumLen).raw()), + fmt::arg("title", title.substr(0, titleLen).raw()), fmt::arg("date", date), fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), From 149c1c2f1b8b4c23f3bc7ea70d9cd503f29d9f31 Mon Sep 17 00:00:00 2001 From: Joshua Chapman Date: Sun, 31 Jan 2021 11:33:17 +0100 Subject: [PATCH 174/355] Update waybar-bluetooth.5.scd Remove the `status` from the `tooltip-format` example since it will throw error. Related to #685 --- man/waybar-bluetooth.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 8151ec0..5d7d7dd 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -85,7 +85,7 @@ Addressed by *bluetooth* "enabled": "", "disabled": "" }, - "tooltip-format": "{status}" + "tooltip-format": "{}" } ``` From 3881af4bbe6412248ac5dc72e55865ae755ee833 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Sun, 31 Jan 2021 15:37:26 -0400 Subject: [PATCH 175/355] Improved wlr/taskbar icon search. --- src/modules/wlr/taskbar.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 4cbb8ce..83ce366 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -26,19 +26,19 @@ const std::string WHITESPACE = " \n\r\t\f\v"; static std::string ltrim(const std::string& s) { - size_t start = s.find_first_not_of(WHITESPACE); - return (start == std::string::npos) ? "" : s.substr(start); + size_t start = s.find_first_not_of(WHITESPACE); + return (start == std::string::npos) ? "" : s.substr(start); } static std::string rtrim(const std::string& s) { - size_t end = s.find_last_not_of(WHITESPACE); - return (end == std::string::npos) ? "" : s.substr(0, end + 1); + size_t end = s.find_last_not_of(WHITESPACE); + return (end == std::string::npos) ? "" : s.substr(0, end + 1); } static std::string trim(const std::string& s) { - return rtrim(ltrim(s)); + return rtrim(ltrim(s)); } @@ -103,8 +103,8 @@ static std::string get_from_desktop_app_info(const std::string &app_id) /* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */ static std::string get_from_icon_theme(const Glib::RefPtr& icon_theme, - const std::string &app_id) { - + const std::string &app_id) +{ if (icon_theme->lookup_icon(app_id, 24)) return app_id; @@ -122,6 +122,10 @@ static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr> app_id) { + size_t start = 0, end = app_id.size(); + start = app_id.rfind(".", end); + std::string app_name = app_id.substr(start+1, app_id.size()); + auto lower_app_id = app_id; std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(), [](char c){ return std::tolower(c); }); @@ -130,10 +134,14 @@ static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr Date: Sun, 31 Jan 2021 11:53:53 -0800 Subject: [PATCH 176/355] Use g_unichar_iswide to properly align calendar on CJK locales --- include/util/ustring_clen.hpp | 5 +++++ meson.build | 1 + src/modules/clock.cpp | 15 ++++++++++----- src/util/ustring_clen.cpp | 9 +++++++++ 4 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 include/util/ustring_clen.hpp create mode 100644 src/util/ustring_clen.cpp diff --git a/include/util/ustring_clen.hpp b/include/util/ustring_clen.hpp new file mode 100644 index 0000000..cddd2e1 --- /dev/null +++ b/include/util/ustring_clen.hpp @@ -0,0 +1,5 @@ +#pragma once +#include + +// calculate column width of ustring +int ustring_clen(const Glib::ustring &str); \ No newline at end of file diff --git a/meson.build b/meson.build index 49e71f2..373ec86 100644 --- a/meson.build +++ b/meson.build @@ -147,6 +147,7 @@ src_files = files( 'src/main.cpp', 'src/bar.cpp', 'src/client.cpp', + 'src/util/ustring_clen.cpp' ) if is_linux diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 5b2c3f4..0aade69 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -5,6 +5,7 @@ #include #include +#include "util/ustring_clen.hpp" #ifdef HAVE_LANGINFO_1STDAY #include #include @@ -154,12 +155,16 @@ auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std do { if (wd != first_dow) os << ' '; Glib::ustring wd_ustring(date::format(locale_, "%a", wd)); - auto wd_len = wd_ustring.length(); - if (wd_len > 2) { - wd_ustring = wd_ustring.substr(0, 2); - wd_len = 2; + auto clen = ustring_clen(wd_ustring); + auto wd_len = wd_ustring.length(); + fmt::print("{} {}\n", clen, wd_len); + while (clen > 2) { + wd_ustring = wd_ustring.substr(0, wd_len-1); + wd_len--; + clen = ustring_clen(wd_ustring); } - const std::string pad(2 - wd_len, ' '); + fmt::print("{} {}", clen, wd_len); + const std::string pad(2 - clen, ' '); os << pad << wd_ustring; } while (++wd != first_dow); os << "\n"; diff --git a/src/util/ustring_clen.cpp b/src/util/ustring_clen.cpp new file mode 100644 index 0000000..cd7d9cf --- /dev/null +++ b/src/util/ustring_clen.cpp @@ -0,0 +1,9 @@ +#include "util/ustring_clen.hpp" + +int ustring_clen(const Glib::ustring &str){ + int total = 0; + for (auto i = str.begin(); i != str.end(); ++i) { + total += g_unichar_iswide(*i) + 1; + } + return total; +} \ No newline at end of file From ecba117dc05b6677c951c4b1c3f1dd2442494aab Mon Sep 17 00:00:00 2001 From: nullobsi Date: Sun, 31 Jan 2021 11:56:25 -0800 Subject: [PATCH 177/355] remove unnessecary logging --- src/modules/clock.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 0aade69..22bedc7 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -157,13 +157,11 @@ auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std Glib::ustring wd_ustring(date::format(locale_, "%a", wd)); auto clen = ustring_clen(wd_ustring); auto wd_len = wd_ustring.length(); - fmt::print("{} {}\n", clen, wd_len); while (clen > 2) { wd_ustring = wd_ustring.substr(0, wd_len-1); wd_len--; clen = ustring_clen(wd_ustring); } - fmt::print("{} {}", clen, wd_len); const std::string pad(2 - clen, ' '); os << pad << wd_ustring; } while (++wd != first_dow); From 8c70513a248fb9c22ff37d14e9a06b983099ab71 Mon Sep 17 00:00:00 2001 From: nullobsi Date: Sun, 31 Jan 2021 13:58:41 -0800 Subject: [PATCH 178/355] add common `align` config property to set text alignment add fixed-length property to set the fixed width of the label --- src/ALabel.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 9371a0e..4d15cc1 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -28,6 +28,15 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st label_.set_single_line_mode(true); } + if (config_["fixed-length"].isUInt()) { + label_.set_width_chars(config_["fixed-length"].asUInt()); + } + + if (config_["align"].isDouble()) { + auto align = config_["align"].asFloat(); + label_.set_xalign(align); + } + if (config_["rotate"].isUInt()) { label_.set_angle(config["rotate"].asUInt()); } From c8d7b6fa92ea006d35b4c425fb38fb8fe83f7d98 Mon Sep 17 00:00:00 2001 From: nullobsi Date: Sun, 31 Jan 2021 14:03:49 -0800 Subject: [PATCH 179/355] rename fixed-length to min-length --- src/ALabel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 4d15cc1..ed5740a 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -20,7 +20,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st } event_box_.add(label_); if (config_["max-length"].isUInt()) { - label_.set_max_width_chars(config_["max-length"].asUInt()); + label_.set_max_width_chars(config_["max-length"].asInt()); label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); label_.set_single_line_mode(true); } else if (ellipsize && label_.get_max_width_chars() == -1) { @@ -28,8 +28,8 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st label_.set_single_line_mode(true); } - if (config_["fixed-length"].isUInt()) { - label_.set_width_chars(config_["fixed-length"].asUInt()); + if (config_["min-length"].isUInt()) { + label_.set_width_chars(config_["min-length"].asUInt()); } if (config_["align"].isDouble()) { From 72cd753c0259f695adc4bf372048256bc403a47d Mon Sep 17 00:00:00 2001 From: nullobsi Date: Mon, 1 Feb 2021 01:44:51 -0800 Subject: [PATCH 180/355] align should use rotate property --- src/ALabel.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index ed5740a..8d9c9b4 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -32,14 +32,24 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st label_.set_width_chars(config_["min-length"].asUInt()); } - if (config_["align"].isDouble()) { - auto align = config_["align"].asFloat(); - label_.set_xalign(align); - } + uint rotate = 0; if (config_["rotate"].isUInt()) { - label_.set_angle(config["rotate"].asUInt()); + rotate = config["rotate"].asUInt(); + label_.set_angle(rotate); } + + if (config_["align"].isDouble()) { + auto align = config_["align"].asFloat(); + if (rotate == 90 || rotate == 270) { + label_.set_yalign(align); + } else { + label_.set_xalign(align); + } + + } + + } auto ALabel::update() -> void { From 97f7050d7defb4dc3e23a1709ce063dbfaa38d9f Mon Sep 17 00:00:00 2001 From: nullobsi Date: Mon, 1 Feb 2021 08:34:51 -0800 Subject: [PATCH 181/355] Update man pages --- man/waybar-backlight.5.scd | 8 ++++++++ man/waybar-battery.5.scd | 8 ++++++++ man/waybar-bluetooth.5.scd | 8 ++++++++ man/waybar-clock.5.scd | 8 ++++++++ man/waybar-cpu.5.scd | 8 ++++++++ man/waybar-custom.5.scd | 8 ++++++++ man/waybar-disk.5.scd | 8 ++++++++ man/waybar-idle-inhibitor.5.scd | 8 ++++++++ man/waybar-memory.5.scd | 8 ++++++++ man/waybar-mpd.5.scd | 8 ++++++++ man/waybar-network.5.scd | 8 ++++++++ man/waybar-pulseaudio.5.scd | 8 ++++++++ man/waybar-sndio.5.scd | 8 ++++++++ man/waybar-sway-language.5.scd | 8 ++++++++ man/waybar-sway-mode.5.scd | 8 ++++++++ man/waybar-sway-window.5.scd | 8 ++++++++ man/waybar-temperature.5.scd | 8 ++++++++ 17 files changed, 136 insertions(+) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index e6116e3..d14e4f2 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -24,6 +24,14 @@ The *backlight* module displays the current backlight level. typeof: integer ++ The maximum length in characters the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index a4650cd..48c2ee1 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -55,6 +55,14 @@ The *battery* module displays the current capacity and state (eg. charging) of y typeof: integer++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *rotate*: ++ typeof: integer++ Positive value to rotate the text label. diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 5d7d7dd..0cd9386 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -35,6 +35,14 @@ Addressed by *bluetooth* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 9f36c43..28688ee 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -45,6 +45,14 @@ The *clock* module displays the current date and time. typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index cb83134..c8b12e2 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -24,6 +24,14 @@ The *cpu* module displays the current cpu utilization. typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 3e820c6..8f9dcfa 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -67,6 +67,14 @@ Addressed by *custom/* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index 431d7c8..5879714 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -39,6 +39,14 @@ Addressed by *disk* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index 9d231d8..0b0bdd0 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -27,6 +27,14 @@ screensaving, also known as "presentation mode". typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. A click also toggles the state diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index 81c6216..3ff4c35 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -34,6 +34,14 @@ Addressed by *memory* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index 8c33c62..5bbc00a 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -81,6 +81,14 @@ Addressed by *mpd* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index ab459ae..f274881 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -64,6 +64,14 @@ Addressed by *network* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index c3f50e0..d894290 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -50,6 +50,14 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *scroll-step*: ++ typeof: float ++ default: 1.0 ++ diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd index a61c332..90a73f4 100644 --- a/man/waybar-sndio.5.scd +++ b/man/waybar-sndio.5.scd @@ -26,6 +26,14 @@ cursor is over the module, and clicking on the module toggles mute. typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *scroll-step*: ++ typeof: int ++ default: 5 ++ diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index a288cca..769924f 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -25,6 +25,14 @@ Addressed by *sway/language* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index 958a1ed..b8b59cd 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -25,6 +25,14 @@ Addressed by *sway/mode* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 4863a76..40250e6 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -25,6 +25,14 @@ Addressed by *sway/window* typeof: integer ++ The maximum length in character the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when clicked on the module. diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 7810a59..8d11e51 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -63,6 +63,14 @@ Addressed by *temperature* typeof: integer ++ The maximum length in characters the module should display. +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ Command to execute when you clicked on the module. From ac6667b1c979aa9493c4ae033d121a712743e484 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Tue, 2 Feb 2021 01:03:28 -0400 Subject: [PATCH 182/355] [wlr/taskbar] More icon search improvements. * Added ~/.local/share prefix to search in user defined apps. * Add support for apps that don't properly set an id like pamac. --- src/modules/wlr/taskbar.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 83ce366..15b927a 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -15,6 +15,7 @@ #include #include +#include #include @@ -64,6 +65,9 @@ static std::vector search_prefix() } while(end != std::string::npos); } + std::string home_dir = std::getenv("HOME"); + prefixes.push_back(home_dir + "/.local/share/"); + for (auto& p : prefixes) spdlog::debug("Using 'desktop' search path prefix: {}", p); @@ -111,6 +115,26 @@ static std::string get_from_icon_theme(const Glib::RefPtr& icon_ return ""; } +/* Method 3 - as last resort perform a search for most appropriate desktop info file */ +static std::string get_from_desktop_app_info_search(const std::string &app_id) +{ + std::string desktop_file = ""; + + gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str()); + if (desktop_list != nullptr && desktop_list[0] != nullptr) { + for (size_t i=0; desktop_list[0][i]; i++) { + if(desktop_file == "") { + desktop_file = desktop_list[0][i]; + } + break; + } + g_strfreev(desktop_list[0]); + } + g_free(desktop_list); + + return get_from_desktop_app_info(desktop_file); +} + static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr& icon_theme, const std::string &app_id_list, int size) { @@ -142,9 +166,11 @@ static bool image_load_icon(Gtk::Image& image, const Glib::RefPtrload_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE); if (pixbuf) { From 7eb2a6b7090c371656dd2df7fd44af6d430b43cb Mon Sep 17 00:00:00 2001 From: Genesis Date: Tue, 2 Feb 2021 21:58:26 +0100 Subject: [PATCH 183/355] Add a configuration entry to disable auto_back_and_forth on Sway workspaces --- include/modules/sway/workspaces.hpp | 2 +- man/waybar-sway-workspaces.5.scd | 12 ++++++++---- src/modules/sway/workspaces.cpp | 14 ++++++++++++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 92ec051..c644383 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -20,7 +20,7 @@ class Workspaces : public AModule, public sigc::trackable { auto update() -> void; private: - static inline const std::string workspace_switch_cmd_ = "workspace --no-auto-back-and-forth \"{}\""; + static inline const std::string workspace_switch_cmd_ = "workspace {} \"{}\""; static int convertWorkspaceNameToNum(std::string name); diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 5e51689..f2808b9 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -66,12 +66,16 @@ Addressed by *sway/workspaces* Lists workspaces that should always be shown, even when non existent *on-update*: ++ - typeof: string ++ - Command to execute when the module is updated. + typeof: string ++ + Command to execute when the module is updated. *numeric-first*: ++ - typeof: bool ++ - Whether to put workspaces starting with numbers before workspaces that do not start with a number. + typeof: bool ++ + Whether to put workspaces starting with numbers before workspaces that do not start with a number. + +*disable-auto-back-and-forth*: ++ + typeof: bool ++ + Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration. # FORMAT REPLACEMENTS diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index d0c2463..43dcf33 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -257,11 +257,19 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { ipc_.sendCmd( IPC_COMMAND, fmt::format(workspace_switch_cmd_ + "; move workspace to output \"{}\"; " + workspace_switch_cmd_, + "--no-auto-back-and-forth", node["name"].asString(), node["target_output"].asString(), + "--no-auto-back-and-forth", node["name"].asString())); } else { - ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, node["name"].asString())); + ipc_.sendCmd( + IPC_COMMAND, + fmt::format("workspace {} \"{}\"", + config_["disable-auto-back-and-forth"].asBool() + ? "--no-auto-back-and-forth" + : "", + node["name"].asString())); } } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); @@ -322,7 +330,9 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { } } try { - ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, name)); + ipc_.sendCmd( + IPC_COMMAND, + fmt::format(workspace_switch_cmd_, "--no-auto-back-and-forth", name)); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } From 22ed153004e904ed58fe3d4817576552ff1fa57b Mon Sep 17 00:00:00 2001 From: jgmdev Date: Wed, 3 Feb 2021 21:04:10 -0400 Subject: [PATCH 184/355] [wlr/taskbar] Fix unhandled exception crash when icon name is a path. --- src/modules/wlr/taskbar.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 15b927a..93c6a77 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -1,5 +1,7 @@ #include "modules/wlr/taskbar.hpp" +#include "glibmm/error.h" +#include "glibmm/fileutils.h" #include "glibmm/refptr.h" #include "util/format.hpp" @@ -74,6 +76,18 @@ static std::vector search_prefix() return prefixes; } +Glib::RefPtr load_icon_from_file(std::string icon_path, int size) +{ + try { + auto pb = Gdk::Pixbuf::create_from_file(icon_path, size, size); + return pb; + } catch(Glib::Error&) { + return {}; + } catch(...) { + return {}; + } +} + /* Method 1 - get the correct icon name from the desktop file */ static std::string get_from_desktop_app_info(const std::string &app_id) { @@ -172,7 +186,17 @@ static bool image_load_icon(Gtk::Image& image, const Glib::RefPtrload_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE); + Glib::RefPtr pixbuf; + + try { + pixbuf = icon_theme->load_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE); + } catch(...) { + if (Glib::file_test(icon_name, Glib::FILE_TEST_EXISTS)) + pixbuf = load_icon_from_file(icon_name, size); + else + pixbuf = {}; + } + if (pixbuf) { image.set(pixbuf); found = true; From 8a284e7c74e25c07a603612393a9a07bf31baee7 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Wed, 3 Feb 2021 21:14:04 -0400 Subject: [PATCH 185/355] [wlr/taskbar] Declared load_icon_from_file() static. --- src/modules/wlr/taskbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 93c6a77..1135a8e 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -76,7 +76,7 @@ static std::vector search_prefix() return prefixes; } -Glib::RefPtr load_icon_from_file(std::string icon_path, int size) +static Glib::RefPtr load_icon_from_file(std::string icon_path, int size) { try { auto pb = Gdk::Pixbuf::create_from_file(icon_path, size, size); From e293b89f6ba08ca92c834bad4473d708530521de Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 4 Feb 2021 04:57:08 -0400 Subject: [PATCH 186/355] [wlr/taskbar] Removed unnecessary catch statement. --- src/modules/wlr/taskbar.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 1135a8e..c2acbd9 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -81,8 +81,6 @@ static Glib::RefPtr load_icon_from_file(std::string icon_path, int try { auto pb = Gdk::Pixbuf::create_from_file(icon_path, size, size); return pb; - } catch(Glib::Error&) { - return {}; } catch(...) { return {}; } From fffb52dd936b7e633996e1a506577c7409cef1fc Mon Sep 17 00:00:00 2001 From: jgmdev Date: Sun, 7 Feb 2021 00:50:52 -0400 Subject: [PATCH 187/355] [wlr/taskbar] Check StartupWMClass on list returned by g_desktop_app_info_searchi() --- src/modules/wlr/taskbar.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index c2acbd9..e91b19a 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -137,8 +137,13 @@ static std::string get_from_desktop_app_info_search(const std::string &app_id) for (size_t i=0; desktop_list[0][i]; i++) { if(desktop_file == "") { desktop_file = desktop_list[0][i]; + } else { + auto tmp_info = Gio::DesktopAppInfo::create(desktop_list[0][i]); + auto startup_class = tmp_info->get_startup_wm_class(); + + if (startup_class == app_id) + desktop_file = desktop_list[0][i]; } - break; } g_strfreev(desktop_list[0]); } From f14a73584f4eeb1c2c761ba293ad616cb7020975 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Sun, 7 Feb 2021 01:01:57 -0400 Subject: [PATCH 188/355] [wlr/taskbar] Added break when matching StartupWMClass is found. --- src/modules/wlr/taskbar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index e91b19a..08e8bf3 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -141,8 +141,10 @@ static std::string get_from_desktop_app_info_search(const std::string &app_id) auto tmp_info = Gio::DesktopAppInfo::create(desktop_list[0][i]); auto startup_class = tmp_info->get_startup_wm_class(); - if (startup_class == app_id) + if (startup_class == app_id) { desktop_file = desktop_list[0][i]; + break; + } } } g_strfreev(desktop_list[0]); From e4a65c72dd8d48a787f065ce330230c45b2e74cb Mon Sep 17 00:00:00 2001 From: jgmdev Date: Sun, 7 Feb 2021 04:27:16 -0400 Subject: [PATCH 189/355] Added missing 'if' space. --- src/modules/wlr/taskbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 08e8bf3..ef46f36 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -135,7 +135,7 @@ static std::string get_from_desktop_app_info_search(const std::string &app_id) gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str()); if (desktop_list != nullptr && desktop_list[0] != nullptr) { for (size_t i=0; desktop_list[0][i]; i++) { - if(desktop_file == "") { + if (desktop_file == "") { desktop_file = desktop_list[0][i]; } else { auto tmp_info = Gio::DesktopAppInfo::create(desktop_list[0][i]); From 65853812306414b525ea6beb25dc915249dc922e Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 8 Feb 2021 22:30:01 -0800 Subject: [PATCH 190/355] fix(client): remove unnecessary wl_output_roundtrip At this point we're not awaiting any protocol events and flushing wayland queue makes little sense. As #1019 shows, it may be even harmful as an extra roundtrip could process wl_output disappearance and delete output object right from under our code. --- src/client.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client.cpp b/src/client.cpp index 0ad4e6b..9983bb5 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -124,7 +124,6 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_ auto configs = client->getOutputConfigs(output); if (!configs.empty()) { - wl_display_roundtrip(client->wl_display); for (const auto &config : configs) { client->bars.emplace_back(std::make_unique(&output, config)); } From 89b5e819a34388e39829bd3cbe52d5fb28a08297 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 8 Feb 2021 23:05:31 -0800 Subject: [PATCH 191/355] fix(client): improve guard from repeated xdg_output.done events Multiple .done events may arrive in batch. In this case libwayland would queue xdg_output.destroy and dispatch all pending events, triggering this callback several times for the same output. Delete xdg_output pointer immediately on the first event and use the value as a guard for reentering. --- src/client.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 9983bb5..fcfcd98 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -120,16 +120,26 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_ auto client = waybar::Client::inst(); try { auto &output = client->getOutput(data); - spdlog::debug("Output detection done: {} ({})", output.name, output.identifier); + /** + * Multiple .done events may arrive in batch. In this case libwayland would queue + * xdg_output.destroy and dispatch all pending events, triggering this callback several times + * for the same output. .done events can also arrive after that for a scale or position changes. + * We wouldn't want to draw a duplicate bar for each such event either. + * + * All the properties we care about are immutable so it's safe to delete the xdg_output object + * on the first event and use the ptr value to check that the callback was already invoked. + */ + if (output.xdg_output) { + output.xdg_output.reset(); + spdlog::debug("Output detection done: {} ({})", output.name, output.identifier); - auto configs = client->getOutputConfigs(output); - if (!configs.empty()) { - for (const auto &config : configs) { - client->bars.emplace_back(std::make_unique(&output, config)); + auto configs = client->getOutputConfigs(output); + if (!configs.empty()) { + for (const auto &config : configs) { + client->bars.emplace_back(std::make_unique(&output, config)); + } } } - // unsubscribe - output.xdg_output.reset(); } catch (const std::exception &e) { std::cerr << e.what() << std::endl; } From 95a6689077b5a42833ba3a228f0e56e7a4b71d66 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Mon, 8 Feb 2021 23:52:29 -0500 Subject: [PATCH 192/355] disable Idle Inhibitor module if unsupported --- src/modules/idle_inhibitor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index 9978bba..26889c2 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -12,6 +12,10 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar_(bar), idle_inhibitor_(nullptr), pid_(-1) { + if (waybar::Client::inst()->idle_inhibit_manager == nullptr) { + throw std::runtime_error("idle-inhibit not available"); + } + event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect( sigc::mem_fun(*this, &IdleInhibitor::handleToggle)); From 40f4dc9ecf26a8d9cd8d33b9bc88ff4dd6b3e508 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 1 Feb 2021 18:50:45 -0800 Subject: [PATCH 193/355] fix(rfkill): accept events larger than v1 event size Kernel 5.11 added one more field to the `struct rfkill_event` and broke unnecessarily strict check in `rfkill.cpp`. According to `linux/rfkill.h`, we must accept events at least as large as v1 event size and should be prepared to get additional fields at the end of a v1 event structure. --- src/util/rfkill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/rfkill.cpp b/src/util/rfkill.cpp index 82d29e9..e968ca1 100644 --- a/src/util/rfkill.cpp +++ b/src/util/rfkill.cpp @@ -61,7 +61,7 @@ void waybar::util::Rfkill::waitForEvent() { break; } - if (len != RFKILL_EVENT_SIZE_V1) { + if (len < RFKILL_EVENT_SIZE_V1) { throw std::runtime_error("Wrong size of RFKILL event"); continue; } From 38c29fc2426557b43af91e7557c40f7880aaa0b8 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 2 Feb 2021 19:17:06 -0800 Subject: [PATCH 194/355] refactor(rfkill): poll rfkill events from Glib main loop Open rfkill device only once per module. Remove rfkill threads and use `Glib::signal_io` as a more efficient way to poll the rfkill device. Handle runtime errors from rfkill and stop polling of the device instead of crashing waybar. --- include/modules/bluetooth.hpp | 16 ++++---- include/modules/network.hpp | 2 - include/util/rfkill.hpp | 15 +++++-- src/modules/bluetooth.cpp | 8 +--- src/modules/network.cpp | 17 ++++---- src/util/rfkill.cpp | 73 ++++++++++++++++++----------------- 6 files changed, 64 insertions(+), 67 deletions(-) diff --git a/include/modules/bluetooth.hpp b/include/modules/bluetooth.hpp index 04c213d..716df0e 100644 --- a/include/modules/bluetooth.hpp +++ b/include/modules/bluetooth.hpp @@ -1,11 +1,11 @@ #pragma once -#include -#include "ALabel.hpp" - #include -#include "util/sleeper_thread.hpp" +#include + +#include "ALabel.hpp" #include "util/rfkill.hpp" +#include "util/sleeper_thread.hpp" namespace waybar::modules { @@ -16,11 +16,9 @@ class Bluetooth : public ALabel { auto update() -> void; private: - std::string status_; - util::SleeperThread thread_; - util::SleeperThread intervall_thread_; - - util::Rfkill rfkill_; + std::string status_; + util::SleeperThread thread_; + util::Rfkill rfkill_; }; } // namespace waybar::modules diff --git a/include/modules/network.hpp b/include/modules/network.hpp index c02d3c5..539f458 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -75,8 +75,6 @@ class Network : public ALabel { util::SleeperThread thread_; util::SleeperThread thread_timer_; #ifdef WANT_RFKILL - util::SleeperThread thread_rfkill_; - util::Rfkill rfkill_; #endif }; diff --git a/include/util/rfkill.hpp b/include/util/rfkill.hpp index ac3d406..5d519ca 100644 --- a/include/util/rfkill.hpp +++ b/include/util/rfkill.hpp @@ -1,19 +1,26 @@ #pragma once +#include #include +#include +#include namespace waybar::util { -class Rfkill { +class Rfkill : public sigc::trackable { public: Rfkill(enum rfkill_type rfkill_type); - ~Rfkill() = default; - void waitForEvent(); + ~Rfkill(); bool getState() const; + sigc::signal on_update; + private: enum rfkill_type rfkill_type_; - int state_ = 0; + bool state_ = false; + int fd_ = -1; + + bool on_event(Glib::IOCondition cond); }; } // namespace waybar::util diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index b390976..9939cc1 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -1,17 +1,11 @@ #include "modules/bluetooth.hpp" -#include "util/rfkill.hpp" -#include -#include waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config) : ALabel(config, "bluetooth", id, "{icon}", 10), status_("disabled"), rfkill_{RFKILL_TYPE_BLUETOOTH} { + rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update))); thread_ = [this] { - dp.emit(); - rfkill_.waitForEvent(); - }; - intervall_thread_ = [this] { 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()); diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 7d9da8b..41bc9d5 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -212,18 +212,15 @@ void waybar::modules::Network::worker() { thread_timer_.sleep_for(interval_); }; #ifdef WANT_RFKILL - thread_rfkill_ = [this] { - rfkill_.waitForEvent(); - { - std::lock_guard lock(mutex_); - if (ifid_ > 0) { - getInfo(); - dp.emit(); - } + rfkill_.on_update.connect([this](auto &) { + std::lock_guard lock(mutex_); + if (ifid_ > 0) { + getInfo(); + dp.emit(); } - }; + }); #else - spdlog::warn("Waybar has been built without rfkill support."); + spdlog::warn("Waybar has been built without rfkill support."); #endif thread_ = [this] { std::array events{}; diff --git a/src/util/rfkill.cpp b/src/util/rfkill.cpp index e968ca1..d3eb516 100644 --- a/src/util/rfkill.cpp +++ b/src/util/rfkill.cpp @@ -19,60 +19,63 @@ #include "util/rfkill.hpp" #include +#include #include -#include -#include +#include #include #include -#include -#include -waybar::util::Rfkill::Rfkill(const enum rfkill_type rfkill_type) : rfkill_type_(rfkill_type) {} - -void waybar::util::Rfkill::waitForEvent() { - struct rfkill_event event; - struct pollfd p; - ssize_t len; - int fd, n; - - fd = open("/dev/rfkill", O_RDONLY); - if (fd < 0) { - throw std::runtime_error("Can't open RFKILL control device"); +waybar::util::Rfkill::Rfkill(const enum rfkill_type rfkill_type) : rfkill_type_(rfkill_type) { + fd_ = open("/dev/rfkill", O_RDONLY); + if (fd_ < 0) { + spdlog::error("Can't open RFKILL control device"); return; } + int rc = fcntl(fd_, F_SETFL, O_NONBLOCK); + if (rc < 0) { + spdlog::error("Can't set RFKILL control device to non-blocking: {}", errno); + close(fd_); + fd_ = -1; + return; + } + Glib::signal_io().connect( + sigc::mem_fun(*this, &Rfkill::on_event), fd_, Glib::IO_IN | Glib::IO_ERR | Glib::IO_HUP); +} - memset(&p, 0, sizeof(p)); - p.fd = fd; - p.events = POLLIN | POLLHUP; +waybar::util::Rfkill::~Rfkill() { + if (fd_ >= 0) { + close(fd_); + } +} - while (1) { - n = poll(&p, 1, -1); - if (n < 0) { - throw std::runtime_error("Failed to poll RFKILL control device"); - break; - } +bool waybar::util::Rfkill::on_event(Glib::IOCondition cond) { + if (cond & Glib::IO_IN) { + struct rfkill_event event; + ssize_t len; - if (n == 0) continue; - - len = read(fd, &event, sizeof(event)); + len = read(fd_, &event, sizeof(event)); if (len < 0) { - throw std::runtime_error("Reading of RFKILL events failed"); - break; + spdlog::error("Reading of RFKILL events failed: {}", errno); + return false; } if (len < RFKILL_EVENT_SIZE_V1) { - throw std::runtime_error("Wrong size of RFKILL event"); - continue; + if (errno != EAGAIN) { + spdlog::error("Wrong size of RFKILL event: {}", len); + } + return true; } - if (event.type == rfkill_type_ && event.op == RFKILL_OP_CHANGE) { + if (event.type == rfkill_type_ && (event.op == RFKILL_OP_ADD || event.op == RFKILL_OP_CHANGE)) { state_ = event.soft || event.hard; - break; + on_update.emit(event); } + return true; + } else { + spdlog::error("Failed to poll RFKILL control device"); + return false; } - - close(fd); } bool waybar::util::Rfkill::getState() const { return state_; } From ecc32ddd185b112b101891200d127dc319a58ca5 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 2 Feb 2021 20:01:01 -0800 Subject: [PATCH 195/355] refactor(bluetooth): remove Bluetooth::status_ The string was always overwritten in `update()`; don't need to store temporary value in the class. --- include/modules/bluetooth.hpp | 4 ---- src/modules/bluetooth.cpp | 21 +++++++-------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/include/modules/bluetooth.hpp b/include/modules/bluetooth.hpp index 716df0e..4d7b7c8 100644 --- a/include/modules/bluetooth.hpp +++ b/include/modules/bluetooth.hpp @@ -1,8 +1,5 @@ #pragma once -#include -#include - #include "ALabel.hpp" #include "util/rfkill.hpp" #include "util/sleeper_thread.hpp" @@ -16,7 +13,6 @@ class Bluetooth : public ALabel { auto update() -> void; private: - std::string status_; util::SleeperThread thread_; util::Rfkill rfkill_; }; diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 9939cc1..0df404d 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -1,9 +1,9 @@ #include "modules/bluetooth.hpp" +#include + waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config) - : ALabel(config, "bluetooth", id, "{icon}", 10), - status_("disabled"), - rfkill_{RFKILL_TYPE_BLUETOOTH} { + : ALabel(config, "bluetooth", id, "{icon}", 10), rfkill_{RFKILL_TYPE_BLUETOOTH} { rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update))); thread_ = [this] { auto now = std::chrono::system_clock::now(); @@ -15,25 +15,18 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& } auto waybar::modules::Bluetooth::update() -> void { - if (rfkill_.getState()) { - status_ = "disabled"; - } else { - status_ = "enabled"; - } + std::string status = rfkill_.getState() ? "disabled" : "enabled"; label_.set_markup( - fmt::format( - format_, - fmt::arg("status", status_), - fmt::arg("icon", getIcon(0, status_)))); + fmt::format(format_, fmt::arg("status", status), fmt::arg("icon", getIcon(0, status)))); if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { auto tooltip_format = config_["tooltip-format"].asString(); - auto tooltip_text = fmt::format(tooltip_format, status_); + auto tooltip_text = fmt::format(tooltip_format, status); label_.set_tooltip_text(tooltip_text); } else { - label_.set_tooltip_text(status_); + label_.set_tooltip_text(status); } } } From 52dd3d2446a99ff822ae4fd913bdab3dc2c06d1c Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 2 Feb 2021 20:10:27 -0800 Subject: [PATCH 196/355] refactor(bluetooth): remove `interval` and timer thread The timer thread was always reading the same value from Rfkill state. --- include/modules/bluetooth.hpp | 4 +--- man/waybar-bluetooth.5.scd | 6 ------ src/modules/bluetooth.cpp | 7 ------- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/include/modules/bluetooth.hpp b/include/modules/bluetooth.hpp index 4d7b7c8..87845c9 100644 --- a/include/modules/bluetooth.hpp +++ b/include/modules/bluetooth.hpp @@ -2,7 +2,6 @@ #include "ALabel.hpp" #include "util/rfkill.hpp" -#include "util/sleeper_thread.hpp" namespace waybar::modules { @@ -13,8 +12,7 @@ class Bluetooth : public ALabel { auto update() -> void; private: - util::SleeperThread thread_; - util::Rfkill rfkill_; + util::Rfkill rfkill_; }; } // namespace waybar::modules diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 0cd9386..d4ecb1d 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -12,11 +12,6 @@ The *bluetooth* module displays information about the status of the device's blu Addressed by *bluetooth* -*interval*: ++ - typeof: integer ++ - default: 60 ++ - The interval in which the bluetooth state gets updated. - *format*: ++ typeof: string ++ default: *{icon}* ++ @@ -88,7 +83,6 @@ Addressed by *bluetooth* "bluetooth": { "format": "{icon}", "format-alt": "bluetooth: {status}", - "interval": 30, "format-icons": { "enabled": "", "disabled": "" diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 0df404d..8852684 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -5,13 +5,6 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config) : ALabel(config, "bluetooth", id, "{icon}", 10), rfkill_{RFKILL_TYPE_BLUETOOTH} { rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update))); - thread_ = [this] { - 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); - dp.emit(); - }; } auto waybar::modules::Bluetooth::update() -> void { From 6d5afdaa5fee87304fceb4b9d978b992bad32126 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 2 Feb 2021 20:56:00 -0800 Subject: [PATCH 197/355] fix(network): don't block the main thread on rfkill update Moving rfkill to the main event loop had unexpected side-effects. Notably, the network module mutex can block all the main thread events for several seconds while the network worker thread is sleeping. Instead of waiting for the mutex let's hope that the worker thread succeeds and schedule timer thread wakeup just in case. --- src/modules/network.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 41bc9d5..a8aaffa 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -213,11 +213,11 @@ void waybar::modules::Network::worker() { }; #ifdef WANT_RFKILL rfkill_.on_update.connect([this](auto &) { - std::lock_guard lock(mutex_); - if (ifid_ > 0) { - getInfo(); - dp.emit(); - } + /* If we are here, it's likely that the network thread already holds the mutex and will be + * holding it for a next few seconds. + * Let's delegate the update to the timer thread instead of blocking the main thread. + */ + thread_timer_.wake_up(); }); #else spdlog::warn("Waybar has been built without rfkill support."); From e786ea601ed0aba54338bb7ea1563f2f3ebd5ae0 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 10 Feb 2021 08:22:22 -0800 Subject: [PATCH 198/355] fix(rfkill): handle EAGAIN correctly --- src/util/rfkill.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/util/rfkill.cpp b/src/util/rfkill.cpp index d3eb516..7400135 100644 --- a/src/util/rfkill.cpp +++ b/src/util/rfkill.cpp @@ -56,14 +56,15 @@ bool waybar::util::Rfkill::on_event(Glib::IOCondition cond) { len = read(fd_, &event, sizeof(event)); if (len < 0) { + if (errno == EAGAIN) { + return true; + } spdlog::error("Reading of RFKILL events failed: {}", errno); return false; } if (len < RFKILL_EVENT_SIZE_V1) { - if (errno != EAGAIN) { - spdlog::error("Wrong size of RFKILL event: {}", len); - } + spdlog::error("Wrong size of RFKILL event: {} < {}", len, RFKILL_EVENT_SIZE_V1); return true; } From d8706af2ea7cb0f668cc26c449b3a9cc46516ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matt=C3=A9o=20Delabre?= Date: Fri, 12 Feb 2021 20:15:38 +0100 Subject: [PATCH 199/355] Terminate custom module scripts on exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (Fixes #358.) Subprocesses created for custom module scripts were previously left running when the parent Waybar process exited. This patch sets the parent-death signal of child processes (PR_SET_PDEATHSIG on Linux, PROC_PDEATHSIG_CTL on FreeBSD) to SIGTERM. Caveats: * This uses Linux-specific or FreeBSD-specific calls. I don’t know if this project targets other systems? * There is a possibility that Waybar exits after calling `fork()`, but before calling `prctl` to set the parent-death signal. In this case, the child will not receive the SIGTERM signal and will continue to run. I did not handle this case as I consider it quite unlikely, since module scripts are usually launched only when Waybar starts. Please let me know if you think it needs to be handled. Testing: * With `htop` open, run Waybar v0.9.5 with a custom module that has an `exec` script. Terminate the Waybar process and notice that the script’s subprocess stays alive and is now a child of the init process. * Run Waybar with this patch and follow the same steps as above. Notice that this time the script’s subprocess terminates when the parent exits. --- include/util/command.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/util/command.hpp b/include/util/command.hpp index 5265558..3a38da3 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -5,6 +5,13 @@ #include #include +#ifdef __linux__ +#include +#endif +#ifdef __FreeBSD__ +#include +#endif + #include extern std::mutex reap_mtx; @@ -77,6 +84,18 @@ inline FILE* open(const std::string& cmd, int& pid) { // Reset sigmask err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr); if (err != 0) spdlog::error("pthread_sigmask in open failed: {}", strerror(err)); + // Kill child if Waybar exits + int deathsig = SIGTERM; +#ifdef __linux__ + if (prctl(PR_SET_PDEATHSIG, deathsig) != 0) { + spdlog::error("prctl(PR_SET_PDEATHSIG) in open failed: {}", strerror(errno)); + } +#endif +#ifdef __FreeBSD__ + if (procctl(P_PID, 0, PROC_PDEATHSIG_CTL, reinterpret_cast(&deathsig)) == -1) { + spdlog::error("procctl(PROC_PDEATHSIG_CTL) in open failed: {}", strerror(errno)); + } +#endif ::close(fd[0]); dup2(fd[1], 1); setpgid(child_pid, child_pid); From 2019028688cb5c03062281854855f05eeb9e606f Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Fri, 19 Feb 2021 14:33:38 +0100 Subject: [PATCH 200/355] Configure systemd.service file to allow reloading This allows `systemctl --user reload waybar` to reload waybar's config as expected. --- resources/waybar.service.in | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/waybar.service.in b/resources/waybar.service.in index af5832d..ef0d07e 100644 --- a/resources/waybar.service.in +++ b/resources/waybar.service.in @@ -6,6 +6,7 @@ After=graphical-session.target [Service] ExecStart=@prefix@/bin/waybar +ExecReload=kill -SIGUSR2 $MAINPID Restart=on-failure [Install] From 943ba3a2da2730d536a2f6409dfe61a8523239a6 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 9 Feb 2021 19:24:46 -0800 Subject: [PATCH 201/355] fix: schedule output destruction on idle callback Defer destruction of bars for the output to the next iteration of the event loop to avoid deleting objects referenced by currently executed code. --- include/client.hpp | 1 + src/client.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/include/client.hpp b/include/client.hpp index 5965f7c..ec3866a 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -51,6 +51,7 @@ class Client { static void handleOutputDescription(void *, struct zxdg_output_v1 *, const char *); void handleMonitorAdded(Glib::RefPtr monitor); void handleMonitorRemoved(Glib::RefPtr monitor); + void handleDeferredMonitorRemoval(Glib::RefPtr monitor); Json::Value config_; Glib::RefPtr style_context_; diff --git a/src/client.cpp b/src/client.cpp index fcfcd98..1c48c81 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -179,6 +179,16 @@ void waybar::Client::handleMonitorAdded(Glib::RefPtr monitor) { void waybar::Client::handleMonitorRemoved(Glib::RefPtr monitor) { spdlog::debug("Output removed: {} {}", monitor->get_manufacturer(), monitor->get_model()); + /* This event can be triggered from wl_display_roundtrip called by GTK or our code. + * Defer destruction of bars for the output to the next iteration of the event loop to avoid + * deleting objects referenced by currently executed code. + */ + Glib::signal_idle().connect_once( + sigc::bind(sigc::mem_fun(*this, &Client::handleDeferredMonitorRemoval), monitor), + Glib::PRIORITY_HIGH_IDLE); +} + +void waybar::Client::handleDeferredMonitorRemoval(Glib::RefPtr monitor) { for (auto it = bars.begin(); it != bars.end();) { if ((*it)->output->monitor == monitor) { auto output_name = (*it)->output->name; From 08ea5ebe1f7f2a3cdeba8f8bbeebcd8539fee359 Mon Sep 17 00:00:00 2001 From: Genesis Date: Tue, 2 Feb 2021 23:33:33 +0100 Subject: [PATCH 202/355] Add cpu frequency --- include/modules/cpu.hpp | 2 ++ man/waybar-cpu.5.scd | 6 ++++++ src/modules/cpu/bsd.cpp | 4 ++++ src/modules/cpu/common.cpp | 21 ++++++++++++++++++++- src/modules/cpu/linux.cpp | 21 +++++++++++++++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 7a70336..6a9b586 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -22,7 +22,9 @@ class Cpu : public ALabel { private: uint16_t getCpuLoad(); std::tuple getCpuUsage(); + std::tuple getCpuFrequency(); std::vector> parseCpuinfo(); + std::vector parseCpuFrequencies(); std::vector> prev_times_; diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index c8b12e2..fbf6206 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -79,6 +79,12 @@ The *cpu* module displays the current cpu utilization. *{usage}*: Current cpu usage. +*{avg_frequency}*: Current cpu average frequency (based on all cores) in GHz. + +*{max_frequency}*: Current cpu max frequency (based on the core with the highest frequency) in GHz. + +*{min_frequency}*: Current cpu min frequency (based on the core with the lowest frequency) in GHz. + # EXAMPLE ``` diff --git a/src/modules/cpu/bsd.cpp b/src/modules/cpu/bsd.cpp index 73ab1e8..10f1838 100644 --- a/src/modules/cpu/bsd.cpp +++ b/src/modules/cpu/bsd.cpp @@ -95,3 +95,7 @@ std::vector> waybar::modules::Cpu::parseCpuinfo() { free(cp_time); return cpuinfo; } + +std::vector waybar::modules::Cpu::parseCpuFrequencies() { + throw std::runtime_error("Cpu frequency is not implemented on BSD."); +} diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index e86d10a..74f1e40 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -12,6 +12,7 @@ auto waybar::modules::Cpu::update() -> void { // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both auto cpu_load = getCpuLoad(); auto [cpu_usage, tooltip] = getCpuUsage(); + auto [max_frequency, min_frequency, avg_frequency] = getCpuFrequency(); if (tooltipEnabled()) { label_.set_tooltip_text(tooltip); } @@ -25,7 +26,12 @@ auto waybar::modules::Cpu::update() -> void { event_box_.hide(); } else { event_box_.show(); - label_.set_markup(fmt::format(format, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage))); + label_.set_markup(fmt::format(format, + fmt::arg("load", cpu_load), + fmt::arg("usage", cpu_usage), + fmt::arg("max_frequency", max_frequency), + fmt::arg("min_frequency", min_frequency), + fmt::arg("avg_frequency", avg_frequency))); } // Call parent update @@ -64,3 +70,16 @@ std::tuple waybar::modules::Cpu::getCpuUsage() { prev_times_ = curr_times; return {usage, tooltip}; } + +std::tuple waybar::modules::Cpu::getCpuFrequency() { + std::vector frequencies = parseCpuFrequencies(); + auto [min, max] = std::minmax_element(std::begin(frequencies), std::end(frequencies)); + float avg_frequency = std::accumulate(std::begin(frequencies), std::end(frequencies), 0.0) / frequencies.size(); + + // Round frequencies with double decimal precision to get GHz + float max_frequency = std::ceil(*max / 10.0) / 100.0; + float min_frequency = std::ceil(*min / 10.0) / 100.0; + avg_frequency = std::ceil(avg_frequency / 10.0) / 100.0; + + return { max_frequency, min_frequency, avg_frequency }; +} diff --git a/src/modules/cpu/linux.cpp b/src/modules/cpu/linux.cpp index 9f1734f..b69f71a 100644 --- a/src/modules/cpu/linux.cpp +++ b/src/modules/cpu/linux.cpp @@ -27,3 +27,24 @@ std::vector> waybar::modules::Cpu::parseCpuinfo() { } return cpuinfo; } + +std::vector waybar::modules::Cpu::parseCpuFrequencies() { + const std::string file_path_ = "/proc/cpuinfo"; + std::ifstream info(file_path_); + if (!info.is_open()) { + throw std::runtime_error("Can't open " + file_path_); + } + std::vector frequencies; + std::string line; + while (getline(info, line)) { + if (line.substr(0, 7).compare("cpu MHz") != 0) { + continue; + } + + std::string frequency_str = line.substr(line.find(":") + 2); + float frequency = std::strtol(frequency_str.c_str(), nullptr, 10); + frequencies.push_back(frequency); + } + info.close(); + return frequencies; +} From 99643ba2e615daf27ae3d8662fc0cd3b122cc664 Mon Sep 17 00:00:00 2001 From: Genesis Date: Sun, 21 Feb 2021 21:57:55 +0100 Subject: [PATCH 203/355] Stub parseCpuFrequencies on *BSD platforms --- src/modules/cpu/bsd.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/modules/cpu/bsd.cpp b/src/modules/cpu/bsd.cpp index 10f1838..a92252f 100644 --- a/src/modules/cpu/bsd.cpp +++ b/src/modules/cpu/bsd.cpp @@ -2,8 +2,10 @@ #include #include +#include #include // malloc #include // sysconf +#include // NAN #if defined(__NetBSD__) || defined(__OpenBSD__) # include @@ -97,5 +99,10 @@ std::vector> waybar::modules::Cpu::parseCpuinfo() { } std::vector waybar::modules::Cpu::parseCpuFrequencies() { - throw std::runtime_error("Cpu frequency is not implemented on BSD."); + static std::vector frequencies; + if (frequencies.empty()) { + spdlog::warn("cpu/bsd: parseCpuFrequencies is not implemented, expect garbage in {*_frequency}"); + frequencies.push_back(NAN); + } + return frequencies; } From 1573e1eb971877f54738f4b5c51dd1c613c1eb80 Mon Sep 17 00:00:00 2001 From: nullobsi Date: Fri, 26 Feb 2021 13:29:58 -0800 Subject: [PATCH 204/355] change variable instead of substr(len) --- src/modules/mpd/mpd.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index ab82224..6a828c3 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -143,26 +143,18 @@ void waybar::modules::MPD::setLabel() { std::string repeatIcon = getOptionIcon("repeat", repeatActivated); bool singleActivated = mpd_status_get_single(status_.get()); std::string singleIcon = getOptionIcon("single", singleActivated); - - auto artistLen = config_["artist-len"].isInt() ? - config_["artist-len"].asInt() : artist.size(); - - auto albumArtistLen = config_["album-artist-len"].isInt() ? - config_["album-artist-len"].asInt() : album_artist.size(); - - auto albumLen = config_["album-len"].isInt() ? - config_["album-len"].asInt() : album.size(); - - auto titleLen = config_["title-len"].isInt() ? - config_["title-len"].asInt() : title.size(); + if (config_["artist-len"].isInt()) artist = artist.substr(0, config_["artist-len"].asInt()); + if (config_["album-artist-len"].isInt()) album_artist = album_artist.substr(0, config_["album-artist-len"].asInt()); + if (config_["album-len"].isInt()) album = album.substr(0, config_["album-len"].asInt()); + if (config_["title-len"].isInt()) title = title.substr(0,config_["title-len"].asInt()); try { label_.set_markup( fmt::format(format, - fmt::arg("artist", Glib::Markup::escape_text(artist.substr(0, artistLen)).raw()), - fmt::arg("albumArtist", Glib::Markup::escape_text(album_artist.substr(0, albumArtistLen)).raw()), - fmt::arg("album", Glib::Markup::escape_text(album.substr(0, albumLen)).raw()), - fmt::arg("title", Glib::Markup::escape_text(title.substr(0, titleLen)).raw()), + 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), @@ -183,10 +175,10 @@ void waybar::modules::MPD::setLabel() { : "MPD (connected)"; try { auto tooltip_text = fmt::format(tooltip_format, - fmt::arg("artist", artist.substr(0, artistLen).raw()), - fmt::arg("albumArtist", album_artist.substr(0, albumArtistLen).raw()), - fmt::arg("album", album.substr(0, albumLen).raw()), - fmt::arg("title", title.substr(0, titleLen).raw()), + fmt::arg("artist", artist.raw()), + fmt::arg("albumArtist", album_artist.raw()), + fmt::arg("album", album.raw()), + fmt::arg("title", title.raw()), fmt::arg("date", date), fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), From a49b12b66b4ff2d8de12ead98caf822d9eb35308 Mon Sep 17 00:00:00 2001 From: Antonin Reitz Date: Fri, 12 Mar 2021 20:58:51 +0100 Subject: [PATCH 205/355] Fix CPU load values --- include/modules/cpu.hpp | 3 +-- src/modules/cpu/common.cpp | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 7a70336..d5b79e0 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -20,7 +19,7 @@ class Cpu : public ALabel { auto update() -> void; private: - uint16_t getCpuLoad(); + double getCpuLoad(); std::tuple getCpuUsage(); std::vector> parseCpuinfo(); diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index e86d10a..03befe4 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -32,10 +32,10 @@ auto waybar::modules::Cpu::update() -> void { ALabel::update(); } -uint16_t waybar::modules::Cpu::getCpuLoad() { +double waybar::modules::Cpu::getCpuLoad() { double load[1]; if (getloadavg(load, 1) != -1) { - return load[0] * 100 / sysconf(_SC_NPROCESSORS_ONLN); + return load[0]; } throw std::runtime_error("Can't get Cpu load"); } From 354de5f13f407ca17e09bdc2aa98dde0821bb758 Mon Sep 17 00:00:00 2001 From: lunik1 Date: Sat, 13 Mar 2021 15:17:11 +0000 Subject: [PATCH 206/355] style: add styling to disk module --- resources/style.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/resources/style.css b/resources/style.css index 920bb52..952f368 100644 --- a/resources/style.css +++ b/resources/style.css @@ -69,6 +69,7 @@ window#waybar.chromium { #battery, #cpu, #memory, +#disk, #temperature, #backlight, #network, @@ -142,6 +143,10 @@ label:focus { background-color: #9b59b6; } +#disk { + background-color: #964B00; +} + #backlight { background-color: #90b1b1; } From b4ee994515e17ffff22e5575316e5b8e57b8cdf1 Mon Sep 17 00:00:00 2001 From: Martin Pittermann Date: Tue, 23 Mar 2021 00:26:45 +0100 Subject: [PATCH 207/355] Add style for battery state "plugged" --- resources/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/style.css b/resources/style.css index 920bb52..e124306 100644 --- a/resources/style.css +++ b/resources/style.css @@ -107,7 +107,7 @@ window#waybar.chromium { color: #000000; } -#battery.charging { +#battery.charging, #battery.plugged { color: #ffffff; background-color: #26A65B; } From f4ad5d36ec0d64f55a541ef4e04500462e2c25ec Mon Sep 17 00:00:00 2001 From: Artur Sinila Date: Wed, 17 Mar 2021 17:07:06 +0300 Subject: [PATCH 208/355] meson.build: add missing waybar-sway-language manpage --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 373ec86..23af767 100644 --- a/meson.build +++ b/meson.build @@ -315,6 +315,7 @@ if scdoc.found() 'waybar-network.5.scd', 'waybar-pulseaudio.5.scd', 'waybar-river-tags.5.scd', + 'waybar-sway-language.5.scd', 'waybar-sway-mode.5.scd', 'waybar-sway-window.5.scd', 'waybar-sway-workspaces.5.scd', From c85021228832cbea002ef38a468b84bbaf36f41c Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Sun, 28 Mar 2021 20:07:35 +0300 Subject: [PATCH 209/355] Use the correct battery status when multiple batteries are present --- src/modules/battery.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 8577469..49e23de 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -135,6 +135,16 @@ void waybar::modules::Battery::refreshBatteries() { } } +// Unknown > Full > Not charging > Discharging > Charging +static bool status_gt(const std::string& a, const std::string& b) { + if (a == b) return false; + else if (a == "Unknown") return true; + else if (a == "Full" && b != "Unknown") return true; + else if (a == "Not charging" && b != "Unknown" && b != "Full") return true; + else if (a == "Discharging" && b != "Unknown" && b != "Full" && b != "Not charging") return true; + return false; +} + const std::tuple waybar::modules::Battery::getInfos() { std::lock_guard guard(battery_list_mutex_); @@ -160,7 +170,9 @@ const std::tuple waybar::modules::Battery::g std::ifstream(bat / full_path) >> energy_full; auto full_design_path = fs::exists(bat / "charge_full_design") ? "charge_full_design" : "energy_full_design"; std::ifstream(bat / full_design_path) >> energy_full_design; - if (_status != "Unknown") { + + // Show the "smallest" status among all batteries + if (status_gt(status, _status)) { status = _status; } total_power += power_now; From 729a4fe37e899143cf9ae2e3bf88c5d574714489 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 15 Apr 2021 16:09:45 +0200 Subject: [PATCH 210/355] chore: v0.9.6 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 23af767..39706ba 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.5', + version: '0.9.6', license: 'MIT', meson_version: '>= 0.49.0', default_options : [ From f8f1e791a30a5b44a43a2d4bf673d837724c5396 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 15 Apr 2021 14:30:29 -0400 Subject: [PATCH 211/355] [Module CPU] fix crash due to empty frequencies. On some systems (eg: ARM) the supported frequencies of the CPU are not properly reported by /proc/cpuinfo so if that fails try to retrieve them from /sys/devices/system/cpu/cpufreq/policy[0-9]/cpuinfo_[max|min]_freq. --- src/modules/cpu/linux.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/modules/cpu/linux.cpp b/src/modules/cpu/linux.cpp index b69f71a..6d638a9 100644 --- a/src/modules/cpu/linux.cpp +++ b/src/modules/cpu/linux.cpp @@ -1,3 +1,4 @@ +#include #include "modules/cpu.hpp" std::vector> waybar::modules::Cpu::parseCpuinfo() { @@ -46,5 +47,31 @@ std::vector waybar::modules::Cpu::parseCpuFrequencies() { frequencies.push_back(frequency); } info.close(); + + if (frequencies.size() <= 0) { + std::string cpufreq_dir = "/sys/devices/system/cpu/cpufreq"; + if (std::filesystem::exists(cpufreq_dir)) { + std::vector frequency_files = { + "/cpuinfo_min_freq", + "/cpuinfo_max_freq" + }; + for (auto& p: std::filesystem::directory_iterator(cpufreq_dir)) { + for (auto freq_file: frequency_files) { + std::string freq_file_path = p.path().string() + freq_file; + if (std::filesystem::exists(freq_file_path)) { + std::string freq_value; + std::ifstream freq(freq_file_path); + if (freq.is_open()) { + getline(freq, freq_value); + float frequency = std::strtol(freq_value.c_str(), nullptr, 10); + frequencies.push_back(frequency / 1000); + freq.close(); + } + } + } + } + } + } + return frequencies; } From 5300461c79de4c38ec652a5e26eb05ab2bbff335 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 15 Apr 2021 21:17:48 +0200 Subject: [PATCH 212/355] chore: v0.9.7 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 39706ba..2bb4e49 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.6', + version: '0.9.7', license: 'MIT', meson_version: '>= 0.49.0', default_options : [ From ba278985e86315731871611996382f31d0e526c2 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 18 Apr 2021 21:34:29 +0300 Subject: [PATCH 213/355] Add ignore-list param to wlr/taskbar --- include/modules/wlr/taskbar.hpp | 7 +++++++ man/waybar-wlr-taskbar.5.scd | 9 ++++++++- src/modules/wlr/taskbar.cpp | 31 ++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 7085d79..bb65066 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -61,6 +62,7 @@ class Task Gtk::Label text_before_; Gtk::Label text_after_; bool button_visible_; + bool ignored_; bool with_icon_; std::string format_before_; @@ -132,10 +134,14 @@ class Taskbar : public waybar::AModule std::vector tasks_; std::vector> icon_themes_; + std::unordered_set ignore_list_; struct zwlr_foreign_toplevel_manager_v1 *manager_; struct wl_seat *seat_; + protected: + + public: /* Callbacks for global registration */ void register_manager(struct wl_registry*, uint32_t name, uint32_t version); @@ -155,6 +161,7 @@ class Taskbar : public waybar::AModule bool all_outputs() const; std::vector> icon_themes() const; + const std::unordered_set& ignore_list() const; }; } /* namespace waybar::modules::wlr */ diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index a2bff26..0e86238 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -68,6 +68,10 @@ Addressed by *wlr/taskbar* typeof: string ++ Command to execute when the module is updated. +*ignore-list*: ++ + typeof: array ++ + List of app_id to be invisible. + # FORMAT REPLACEMENTS *{icon}*: The icon of the application. @@ -98,7 +102,10 @@ Addressed by *wlr/taskbar* "icon-theme": "Numix-Circle", "tooltip-format": "{title}", "on-click": "activate", - "on-click-middle": "close" + "on-click-middle": "close", + "ignore-list": [ + "Alacritty" + ] } ``` diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index ef46f36..932a95e 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -277,7 +277,7 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, bar_{bar}, config_{config}, tbar_{tbar}, handle_{tl_handle}, seat_{seat}, id_{global_id++}, content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, - button_visible_{false} + button_visible_{false}, ignored_{false} { zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this); @@ -383,6 +383,21 @@ void Task::handle_app_id(const char *app_id) { app_id_ = app_id; + if (tbar_->ignore_list().count(app_id)) { + ignored_ = true; + if (button_visible_) { + auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + handle_output_leave(output); + } + } else { + bool is_was_ignored = ignored_; + ignored_ = false; + if (is_was_ignored) { + auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + handle_output_enter(output); + } + } + if (!with_icon_) return; @@ -405,6 +420,11 @@ void Task::handle_output_enter(struct wl_output *output) { spdlog::debug("{} entered output {}", repr(), (void*)output); + if (ignored_) { + spdlog::debug("{} is ignored", repr()); + return; + } + if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) { /* The task entered the output of the current bar make the button visible */ tbar_->add_button(button_); @@ -694,6 +714,14 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu icon_themes_.push_back(it); } + + // Load ignore-list + if (config_["ignore-list"].isArray()) { + for (auto& app_name : config_["ignore-list"]) { + ignore_list_.emplace(app_name.asString()); + } + } + icon_themes_.push_back(Gtk::IconTheme::get_default()); } @@ -829,5 +857,6 @@ std::vector> Taskbar::icon_themes() const { return icon_themes_; } +const std::unordered_set &Taskbar::ignore_list() const { return ignore_list_; } } /* namespace waybar::modules::wlr */ From 5ad3b6018a85be29a88131bfe1e8270589140f5b Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 18 Apr 2021 21:37:58 +0300 Subject: [PATCH 214/355] Remove exceed protected --- include/modules/wlr/taskbar.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index bb65066..891ad55 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -139,9 +139,6 @@ class Taskbar : public waybar::AModule struct zwlr_foreign_toplevel_manager_v1 *manager_; struct wl_seat *seat_; - protected: - - public: /* Callbacks for global registration */ void register_manager(struct wl_registry*, uint32_t name, uint32_t version); From fc89b01ba68b13321e2254158247b7022dc370f5 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Tue, 20 Apr 2021 08:25:48 +0200 Subject: [PATCH 215/355] feat: implement mpd volume format template Allow the user to show the current volume from MPD status via the `format` and/or `tooltip-format` configuration options. The values are provided by libmpdclient and are integers, generally between 0-100 (without %). Values above 100 are also possible, as mpd output plugins like `pulse` support volumes above 100%. Signed-off-by: Sefa Eyeoglu --- man/waybar-mpd.5.scd | 2 ++ resources/config | 2 +- src/modules/mpd/mpd.cpp | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index b8e9664..044af98 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -172,6 +172,8 @@ Addressed by *mpd* *{date}*: The date of the current song +*{volume}*: The current volume in percent + *{elapsedTime}*: The current position of the current song. To format as a date/time (see example configuration) *{totalTime}*: The length of the current song. To format as a date/time (see example configuration) diff --git a/resources/config b/resources/config index f3c0a77..13dc94c 100644 --- a/resources/config +++ b/resources/config @@ -27,7 +27,7 @@ "format": "{}" }, "mpd": { - "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ ", + "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ", "format-disconnected": "Disconnected ", "format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ", "unknown-tag": "N/A", diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 6a828c3..0a7c970 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -100,7 +100,7 @@ void waybar::modules::MPD::setLabel() { auto format = format_; Glib::ustring artist, album_artist, album, title; std::string date; - int song_pos = 0, queue_length = 0; + int song_pos = 0, queue_length = 0, volume = 0; std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; @@ -130,6 +130,7 @@ void waybar::modules::MPD::setLabel() { title = getTag(MPD_TAG_TITLE); date = getTag(MPD_TAG_DATE); song_pos = mpd_status_get_song_pos(status_.get()); + volume = mpd_status_get_volume(status_.get()); queue_length = mpd_status_get_queue_length(status_.get()); elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get())); totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get())); @@ -156,6 +157,7 @@ void waybar::modules::MPD::setLabel() { 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("volume", volume), fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), fmt::arg("songPosition", song_pos), @@ -180,6 +182,7 @@ void waybar::modules::MPD::setLabel() { fmt::arg("album", album.raw()), fmt::arg("title", title.raw()), fmt::arg("date", date), + fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), fmt::arg("songPosition", song_pos), From b16c8972c70d536df0e579beee8c9054f0990d6b Mon Sep 17 00:00:00 2001 From: Matthias Richter Date: Sun, 21 Mar 2021 11:42:25 +0100 Subject: [PATCH 216/355] Add option to rewrite sway/window title MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrites window title according to config option "rewrite". "rewrite" is an object where keys are regular expressions and values are rewrite rules if the expression matches. Rules may contain references to captures of the expression. Regex and replacement follow ECMA-script rules. If no regex matches, the title is left unchanged. example: "sway/window": { "rewrite": { "(.*) - Mozilla Firefox": " $1", "(.*) - zsh": " $1", } } --- include/modules/sway/window.hpp | 1 + src/modules/sway/window.cpp | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/include/modules/sway/window.hpp b/include/modules/sway/window.hpp index 40aaa1a..0f7ae31 100644 --- a/include/modules/sway/window.hpp +++ b/include/modules/sway/window.hpp @@ -22,6 +22,7 @@ class Window : public ALabel, public sigc::trackable { std::tuple getFocusedNode(const Json::Value& nodes, std::string& output); void getTree(); + std::string rewriteTitle(const std::string& title); const Bar& bar_; std::string window_; diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index d8f113f..3b424c2 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -1,5 +1,6 @@ #include "modules/sway/window.hpp" #include +#include namespace waybar::modules::sway { @@ -56,7 +57,7 @@ auto Window::update() -> void { bar_.window.get_style_context()->remove_class("solo"); bar_.window.get_style_context()->remove_class("empty"); } - label_.set_markup(fmt::format(format_, fmt::arg("title", window_), + label_.set_markup(fmt::format(format_, fmt::arg("title", rewriteTitle(window_)), fmt::arg("app_id", app_id_))); if (tooltipEnabled()) { label_.set_tooltip_text(window_); @@ -131,4 +132,23 @@ void Window::getTree() { } } +std::string Window::rewriteTitle(const std::string& title) +{ + const auto& rules = config_["rewrite"]; + if (!rules.isObject()) { + return title; + } + + for (auto it = rules.begin(); it != rules.end(); ++it) { + if (it.key().isString() && it->isString()) { + const std::regex rule{it.key().asString()}; + if (std::regex_match(title, rule)) { + return std::regex_replace(title, rule, it->asString()); + } + } + } + + return title; +} + } // namespace waybar::modules::sway From af3c868a5b63b1e77b56801ca558f76afcc21ed6 Mon Sep 17 00:00:00 2001 From: Matthias Richter Date: Wed, 21 Apr 2021 12:15:25 +0200 Subject: [PATCH 217/355] Catch exception on erroneous rules std::regex and std::regex_replace may throw an std::regex_error if the expression or replacement contain errors. Log this error and carry on with the next rule, so that the title is shown even if the config contains errors. --- src/modules/sway/window.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 3b424c2..0491039 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -141,9 +141,15 @@ std::string Window::rewriteTitle(const std::string& title) for (auto it = rules.begin(); it != rules.end(); ++it) { if (it.key().isString() && it->isString()) { - const std::regex rule{it.key().asString()}; - if (std::regex_match(title, rule)) { - return std::regex_replace(title, rule, it->asString()); + try { + // malformated regexes will cause an exception. + // in this case, log error and try the next rule. + const std::regex rule{it.key().asString()}; + if (std::regex_match(title, rule)) { + return std::regex_replace(title, rule, it->asString()); + } + } catch (const std::regex_error& e) { + spdlog::error("Invalid rule {}: {}", it.key().asString(), e.what()); } } } From 7cdf178f8d7c3caec57b57bd2f539e4982d83a9a Mon Sep 17 00:00:00 2001 From: Matthias Richter Date: Wed, 21 Apr 2021 12:18:33 +0200 Subject: [PATCH 218/355] Document changes in manpage Add section on rewrite rules and extend example --- man/waybar-sway-window.5.scd | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 40250e6..ea06069 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -66,12 +66,32 @@ Addressed by *sway/window* default: true ++ Option to disable tooltip on hover. +*rewrite*: ++ + typeof: object ++ + Rules to rewrite window title. See *rewrite rules*. + +# REWRITE RULES + +*rewrite* is an object where keys are regular expressions and values are +rewrite rules if the expression matches. Rules may contain references to +captures of the expression. + +Regular expression and replacement follow ECMA-script rules. + +If no expression matches, the title is left unchanged. + +Invalid expressions (e.g., mismatched parentheses) are skipped. + # EXAMPLES ``` "sway/window": { "format": "{}", - "max-length": 50 + "max-length": 50, + "rewrite": { + "(.*) - Mozilla Firefox": "🌎 $1", + "(.*) - zsh": "> [$1]" + } } ``` From 2213380dc0c89b104b92183249c48c27a2b16f72 Mon Sep 17 00:00:00 2001 From: David96 Date: Sun, 25 Apr 2021 11:19:35 +0200 Subject: [PATCH 219/355] [modules/pulseaudio] fix bluetooth class for PipeWire apparently, pipewire-pulse slightly changed the naming of the sink. --- src/modules/pulseaudio.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 7f4f3b6..3fbe956 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -210,7 +210,8 @@ auto waybar::modules::Pulseaudio::update() -> void { std::string tooltip_format; if (!alt_) { std::string format_name = "format"; - if (monitor_.find("a2dp_sink") != std::string::npos) { + if (monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio + monitor_.find("a2dp-sink") != std::string::npos) { // PipeWire format_name = format_name + "-bluetooth"; label_.get_style_context()->add_class("bluetooth"); } else { From 7e13e26c29f2e7090d3d92a7016a0cf2b2af6746 Mon Sep 17 00:00:00 2001 From: Gabe Gorelick Date: Sun, 25 Apr 2021 22:00:24 -0400 Subject: [PATCH 220/355] [modules/battery] allow format-discharging-full `format-discharging-full` has been impossible since #923 made it impossible to be full and discharging at the same time. This should fix that by only making `format-charging-full` impossible. Whether or not that should be allowed is a good question, but beyond the scope of this change. Fixes #1031 --- src/modules/battery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 49e23de..e598858 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -216,7 +216,7 @@ const std::tuple waybar::modules::Battery::g capacity = 100.f; } uint8_t cap = round(capacity); - if (cap == 100) { + if (cap == 100 && status == "Charging") { // If we've reached 100% just mark as full as some batteries can stay // stuck reporting they're still charging but not yet done status = "Full"; From a03283d65f0dff17b5e69258c5cc206bdfd5f8c6 Mon Sep 17 00:00:00 2001 From: Patrick Hilhorst Date: Mon, 26 Apr 2021 20:26:43 +0200 Subject: [PATCH 221/355] rewriteTitle: allow multiple sequential rewrites --- src/modules/sway/window.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 0491039..b677958 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -132,13 +132,14 @@ void Window::getTree() { } } -std::string Window::rewriteTitle(const std::string& title) -{ +std::string Window::rewriteTitle(const std::string& title) { const auto& rules = config_["rewrite"]; if (!rules.isObject()) { return title; } + std::string res = title; + for (auto it = rules.begin(); it != rules.end(); ++it) { if (it.key().isString() && it->isString()) { try { @@ -146,7 +147,7 @@ std::string Window::rewriteTitle(const std::string& title) // in this case, log error and try the next rule. const std::regex rule{it.key().asString()}; if (std::regex_match(title, rule)) { - return std::regex_replace(title, rule, it->asString()); + res = std::regex_replace(res, rule, it->asString()); } } catch (const std::regex_error& e) { spdlog::error("Invalid rule {}: {}", it.key().asString(), e.what()); @@ -154,7 +155,7 @@ std::string Window::rewriteTitle(const std::string& title) } } - return title; + return res; } } // namespace waybar::modules::sway From 71d7596b6f5537a5572a2588320d85e37fa5f670 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 30 Apr 2021 14:23:49 +0200 Subject: [PATCH 222/355] fix: bluetooth status tooltip --- src/modules/bluetooth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 8852684..1540c05 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -16,7 +16,7 @@ auto waybar::modules::Bluetooth::update() -> void { if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { auto tooltip_format = config_["tooltip-format"].asString(); - auto tooltip_text = fmt::format(tooltip_format, status); + auto tooltip_text = fmt::format(tooltip_format, status, fmt::arg("status", status)); label_.set_tooltip_text(tooltip_text); } else { label_.set_tooltip_text(status); From b25b7d29fcd8c80b49bba5a02a69825811719699 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 30 Apr 2021 14:25:26 +0200 Subject: [PATCH 223/355] Update spdlog.wrap --- subprojects/spdlog.wrap | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/subprojects/spdlog.wrap b/subprojects/spdlog.wrap index c30450e..daddfd6 100644 --- a/subprojects/spdlog.wrap +++ b/subprojects/spdlog.wrap @@ -1,13 +1,11 @@ [wrap-file] -directory = spdlog-1.8.1 - -source_url = https://github.com/gabime/spdlog/archive/v1.8.1.tar.gz -source_filename = v1.8.1.tar.gz -source_hash = 5197b3147cfcfaa67dd564db7b878e4a4b3d9f3443801722b3915cdeced656cb - -patch_url = https://github.com/mesonbuild/spdlog/releases/download/1.8.1-1/spdlog.zip -patch_filename = spdlog-1.8.1-1-wrap.zip -patch_hash = 76844292a8e912aec78450618271a311841b33b17000988f215ddd6c64dd71b3 +directory = spdlog-1.8.5 +source_url = https://github.com/gabime/spdlog/archive/v1.8.5.tar.gz +source_filename = v1.8.5.tar.gz +source_hash = 944d0bd7c763ac721398dca2bb0f3b5ed16f67cef36810ede5061f35a543b4b8 +patch_url = https://wrapdb.mesonbuild.com/v1/projects/spdlog/1.8.5/1/get_zip +patch_filename = spdlog-1.8.5-1-wrap.zip +patch_hash = 3c38f275d5792b1286391102594329e98b17737924b344f98312ab09929b74be [provide] spdlog = spdlog_dep From cdce3e03ea9c22ef8825d53001b84520e3e32c98 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 30 Apr 2021 14:25:48 +0200 Subject: [PATCH 224/355] Update meson.build --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 2bb4e49..e80448e 100644 --- a/meson.build +++ b/meson.build @@ -80,7 +80,7 @@ is_openbsd = host_machine.system() == 'openbsd' thread_dep = dependency('threads') fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep']) -spdlog = dependency('spdlog', version : ['>=1.8.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true']) +spdlog = dependency('spdlog', version : ['>=1.8.5'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') From f3a6e2d49481ac761b3e4886cfad4c2d721ded1b Mon Sep 17 00:00:00 2001 From: Max1Truc Date: Mon, 10 May 2021 21:00:14 +0200 Subject: [PATCH 225/355] fix: incorrect battery percentage on Pinebook Pro --- src/modules/battery.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index e598858..67223c4 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -164,13 +164,25 @@ const std::tuple waybar::modules::Battery::g 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::string now_path; + if (fs::exists(bat / "charge_now")) { + now_path = "charge_now"; + } else if (fs::exists(bat / "energy_now")) { + now_path = "energy_now"; + } else { + now_path = "capacity"; + } 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; auto full_design_path = fs::exists(bat / "charge_full_design") ? "charge_full_design" : "energy_full_design"; std::ifstream(bat / full_design_path) >> energy_full_design; + if (now_path == "capacity") { + energy_now = energy_now * energy_full / 100; + } + // Show the "smallest" status among all batteries if (status_gt(status, _status)) { status = _status; @@ -245,7 +257,7 @@ const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) c } const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemaining) { - hoursRemaining = std::fabs(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"); From 4d067619a8f72aebd64042db9d686c6ef15a0a3e Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Sat, 15 May 2021 15:46:23 +0100 Subject: [PATCH 226/355] =?UTF-8?q?Fix=20power=20calculation=20when=20batt?= =?UTF-8?q?ery=20units=20are=20in=20=CE=BCA=20instead=20of=20=CE=BCW?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/battery.cpp | 45 +++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 67223c4..2656769 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -162,25 +162,36 @@ const std::tuple waybar::modules::Battery::g uint32_t energy_full_design; std::string _status; std::ifstream(bat / "status") >> _status; - auto rate_path = fs::exists(bat / "current_now") ? "current_now" : "power_now"; - std::ifstream(bat / rate_path) >> power_now; - std::string now_path; - if (fs::exists(bat / "charge_now")) { - now_path = "charge_now"; - } else if (fs::exists(bat / "energy_now")) { - now_path = "energy_now"; + // Some battery will report current and charge in μA/μAh. + // Scale these by the voltage to get μW/μWh. + if (fs::exists(bat / "current_now")) { + uint32_t voltage_now; + uint32_t current_now; + uint32_t charge_now; + uint32_t charge_full; + uint32_t charge_full_design; + std::ifstream(bat / "voltage_now") >> voltage_now; + std::ifstream(bat / "current_now") >> current_now; + std::ifstream(bat / "charge_full") >> charge_full; + std::ifstream(bat / "charge_full_design") >> charge_full_design; + if (fs::exists(bat / "charge_now")) + std::ifstream(bat / "charge_now") >> charge_now; + else { + // charge_now is missing on some systems, estimate using capacity. + uint32_t capacity; + std::ifstream(bat / "capacity") >> capacity; + charge_now = (capacity * charge_full) / 100; + } + power_now = ((uint64_t)current_now * (uint64_t)voltage_now) / 1000000; + energy_now = ((uint64_t)charge_now * (uint64_t)voltage_now) / 1000000; + energy_full = ((uint64_t)charge_full * (uint64_t)voltage_now) / 1000000; + energy_full_design = ((uint64_t)charge_full_design * (uint64_t)voltage_now) / 1000000; } else { - now_path = "capacity"; - } - 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; - auto full_design_path = fs::exists(bat / "charge_full_design") ? "charge_full_design" : "energy_full_design"; - std::ifstream(bat / full_design_path) >> energy_full_design; - - if (now_path == "capacity") { - energy_now = energy_now * energy_full / 100; + std::ifstream(bat / "power_now") >> power_now; + std::ifstream(bat / "energy_now") >> energy_now; + std::ifstream(bat / "energy_full") >> energy_full; + std::ifstream(bat / "energy_full_design") >> energy_full_design; } // Show the "smallest" status among all batteries From dbc06abf1826a3c5752a39f1843144a0cc2a87a1 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 227/355] network: Initialise cidr_ like clearIface() does --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index a8aaffa..8dcfb57 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -86,7 +86,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), ev_fd_(-1), - cidr_(-1), + cidr_(0), signal_strength_dbm_(0), signal_strength_(0), #ifdef WANT_RFKILL From 9357a6cb8802b3d57a6c140a3d9a9a2068403089 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 228/355] network: Start the module with some text in the label_ Fix modules starting with no text, but not hidding. Start with some "text" in the module's label_, update() will then update it. Since the text should be different, update() will be able to show or hide the event_box_. This is to work around the case where the module start with no text, but the the event_box_ is shown. --- src/modules/network.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 8dcfb57..e52ed02 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -93,6 +93,13 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf rfkill_{RFKILL_TYPE_WLAN}, #endif frequency_(0) { + + // Start with some "text" in the module's label_, update() will then + // update it. Since the text should be different, update() will be able + // to show or hide the event_box_. This is to work around the case where + // the module start with no text, but the the event_box_ is shown. + label_.set_markup(""); + 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) { From 63fdf66ad66914354d4ac510f08d51dd25389c36 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 229/355] network: Read all available messages on ev_sock_ When more than one message is available to read on the ev_sock_ socket, only the first one is read. Make some changes to be able to read all the messages available by setting the socket to non-blocking. This way we can detect when there's nothing left to read and loop back to wait with epoll. --- src/modules/network.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index e52ed02..bd84c1a 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -160,6 +160,9 @@ void waybar::modules::Network::createEventSocket() { if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) { throw std::runtime_error("Can't connect network socket"); } + if (nl_socket_set_nonblocking(ev_sock_)) { + throw std::runtime_error("Can't set non-blocking on network socket"); + } nl_socket_add_membership(ev_sock_, RTNLGRP_LINK); if (family_ == AF_INET) { nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR); @@ -235,7 +238,23 @@ void waybar::modules::Network::worker() { 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) { + if (events[i].data.fd == nl_socket_get_fd(ev_sock_)) { + int rc = 0; + // Read as many message as possible, until the socket blocks + while (true) { + errno = 0; + rc = nl_recvmsgs_default(ev_sock_); + if (rc == -NLE_AGAIN || errno == EAGAIN) { + rc = 0; + break; + } + } + if (rc < 0) { + spdlog::error("nl_recvmsgs_default error: {}", nl_geterror(-rc)); + thread_.stop(); + break; + } + } else { thread_.stop(); break; } From c9bbaa7241bb40f06bed523c54faa9a8643f7ec2 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 230/355] network: Rework initial interface search by using a dump Instead of using an alternative way to list all links in order to choose one when an "interface" is in the configuration, we can ask for a dump of all interface an reuse the handleEvents() function. This patch also start to rework the handleEvents() function to grab more information out of each event, like the interface name. --- include/modules/network.hpp | 6 ++ src/modules/network.cpp | 130 ++++++++++++++++++++++++------------ 2 files changed, 95 insertions(+), 41 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 539f458..2d3a1ff 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -28,8 +28,11 @@ class Network : public ALabel { static const uint8_t EPOLL_MAX = 200; static int handleEvents(struct nl_msg*, void*); + static int handleEventsDone(struct nl_msg*, void*); static int handleScan(struct nl_msg*, void*); + void askForStateDump(void); + void worker(); void createInfoSocket(); void createEventSocket(); @@ -59,6 +62,9 @@ class Network : public ALabel { int nl80211_id_; std::mutex mutex_; + bool want_link_dump_; + bool dump_in_progress_; + unsigned long long bandwidth_down_total_; unsigned long long bandwidth_up_total_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index bd84c1a..65d82b6 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -86,6 +86,8 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), ev_fd_(-1), + want_link_dump_(false), + dump_in_progress_(false), cidr_(0), signal_strength_dbm_(0), signal_strength_(0), @@ -114,6 +116,11 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf bandwidth_up_total_ = 0; } + if (config_["interface"].isString()) { + // Look for an interface that match "interface" + want_link_dump_ = true; + } + createEventSocket(); createInfoSocket(); auto default_iface = getPreferredIface(-1, false); @@ -125,6 +132,10 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf getInterfaceAddress(); } dp.emit(); + // Ask for a dump of interfaces and then addresses to populate our + // information. First the interface dump, and once done, the callback + // will be called again which will ask for addresses dump. + askForStateDump(); worker(); } @@ -155,6 +166,7 @@ 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); + nl_socket_modify_cb(ev_sock_, NL_CB_FINISH, NL_CB_CUSTOM, handleEventsDone, this); auto groups = RTMGRP_LINK | (family_ == AF_INET ? RTMGRP_IPV4_IFADDR : RTMGRP_IPV6_IFADDR); nl_join_groups(ev_sock_, groups); // Deprecated if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) { @@ -590,28 +602,8 @@ bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::strin 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; - } + // Using link dump instead + return -1; } // getExternalInterface may need some delay to detect external interface for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) { @@ -656,6 +648,55 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { std::lock_guard lock(net->mutex_); auto nh = nlmsg_hdr(msg); auto ifi = static_cast(NLMSG_DATA(nh)); + bool is_del_event = false; + + switch (nh->nlmsg_type) { + case RTM_DELLINK: + is_del_event = true; + case RTM_NEWLINK: { + struct ifinfomsg *ifi = static_cast(NLMSG_DATA(nh)); + ssize_t attrlen = IFLA_PAYLOAD(nh); + struct rtattr *ifla = IFLA_RTA(ifi); + const char *ifname = NULL; + size_t ifname_len = 0; + + if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) { + return NL_OK; + } + + for (; RTA_OK(ifla, attrlen); ifla = RTA_NEXT(ifla, attrlen)) { + switch (ifla->rta_type) { + case IFLA_IFNAME: + ifname = static_cast(RTA_DATA(ifla)); + ifname_len = RTA_PAYLOAD(ifla) - 1; // minus \0 + break; + } + } + + if (!is_del_event && net->ifid_ == -1) { + // Checking if it's an interface we care about. + std::string new_ifname (ifname, ifname_len); + if (net->checkInterface(ifi, new_ifname)) { + spdlog::debug("network: selecting new interface {}/{}", new_ifname, ifi->ifi_index); + + net->ifname_ = new_ifname; + net->ifid_ = ifi->ifi_index; + net->getInterfaceAddress(); + net->thread_timer_.wake_up(); + } + } else if (is_del_event && net->ifid_ >= 0) { + // Our interface has been deleted, start looking/waiting for one we care. + spdlog::debug("network: interface {}/{} deleted", net->ifname_, net->ifid_); + + net->clearIface(); + // Check for a new interface and get info + net->checkNewInterface(ifi); + net->dp.emit(); + } + return NL_OK; + } + } + if (nh->nlmsg_type == RTM_DELADDR) { // Check for valid interface if (ifi->ifi_index == net->ifid_) { @@ -671,25 +712,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } 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); @@ -713,6 +735,32 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { return NL_SKIP; } +void waybar::modules::Network::askForStateDump(void) { + /* We need to wait until the current dump is done before sending new + * messages. handleEventsDone() is called when a dump is done. */ + if (dump_in_progress_) + return; + + struct rtgenmsg rt_hdr = { + .rtgen_family = AF_UNSPEC, + }; + + if (want_link_dump_) { + nl_send_simple(ev_sock_, RTM_GETLINK, NLM_F_DUMP, + &rt_hdr, sizeof (rt_hdr)); + want_link_dump_ = false; + dump_in_progress_ = true; + + } +} + +int waybar::modules::Network::handleEventsDone(struct nl_msg *msg, void *data) { + auto net = static_cast(data); + net->dump_in_progress_ = false; + net->askForStateDump(); + return NL_OK; +} + 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))); From 0fc7ef6685321e1b7d966c21c508e94e6fbf3692 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 231/355] network: Rework address lookup to use only events In order to get the IP address of an interface, we can get the information out of NEWADDR events without needed to call getifaddrs(). And when now events are expected, we can requests a dump of all addresses and handle addresses changes the same way via handleEvents() only. --- include/modules/network.hpp | 3 +- src/modules/network.cpp | 176 ++++++++++++++++++------------------ 2 files changed, 87 insertions(+), 92 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 2d3a1ff..cc8b3f5 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -37,7 +36,6 @@ class Network : public ALabel { 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**); @@ -63,6 +61,7 @@ class Network : public ALabel { std::mutex mutex_; bool want_link_dump_; + bool want_addr_dump_; bool dump_in_progress_; unsigned long long bandwidth_down_total_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 65d82b6..f4cee78 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -87,6 +87,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf efd_(-1), ev_fd_(-1), want_link_dump_(false), + want_addr_dump_(false), dump_in_progress_(false), cidr_(0), signal_strength_dbm_(0), @@ -118,7 +119,9 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf if (config_["interface"].isString()) { // Look for an interface that match "interface" + // and then find the address associated with it. want_link_dump_ = true; + want_addr_dump_ = true; } createEventSocket(); @@ -129,7 +132,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf char ifname[IF_NAMESIZE]; if_indextoname(default_iface, ifname); ifname_ = ifname; - getInterfaceAddress(); + want_addr_dump_ = true; } dp.emit(); // Ask for a dump of interfaces and then addresses to populate our @@ -502,55 +505,6 @@ out: return ifidx; } -void waybar::modules::Network::getInterfaceAddress() { - struct ifaddrs *ifaddr, *ifa; - cidr_ = 0; - int success = getifaddrs(&ifaddr); - if (success != 0) { - return; - } - ifa = ifaddr; - while (ifa != nullptr) { - if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ && - ifa->ifa_name == ifname_) { - char ipaddr[INET6_ADDRSTRLEN]; - char netmask[INET6_ADDRSTRLEN]; - unsigned int cidr = 0; - if (family_ == AF_INET) { - ipaddr_ = inet_ntop(AF_INET, - &reinterpret_cast(ifa->ifa_addr)->sin_addr, - ipaddr, - INET_ADDRSTRLEN); - auto net_addr = reinterpret_cast(ifa->ifa_netmask); - netmask_ = inet_ntop(AF_INET, &net_addr->sin_addr, netmask, INET_ADDRSTRLEN); - unsigned int cidrRaw = net_addr->sin_addr.s_addr; - while (cidrRaw) { - cidr += cidrRaw & 1; - cidrRaw >>= 1; - } - } else { - ipaddr_ = inet_ntop(AF_INET6, - &reinterpret_cast(ifa->ifa_addr)->sin6_addr, - ipaddr, - INET6_ADDRSTRLEN); - auto net_addr = reinterpret_cast(ifa->ifa_netmask); - netmask_ = inet_ntop(AF_INET6, &net_addr->sin6_addr, netmask, INET6_ADDRSTRLEN); - for (size_t i = 0; i < sizeof(net_addr->sin6_addr.s6_addr); ++i) { - unsigned char cidrRaw = net_addr->sin6_addr.s6_addr[i]; - while (cidrRaw) { - cidr += cidrRaw & 1; - cidrRaw >>= 1; - } - } - } - cidr_ = cidr; - break; - } - ifa = ifa->ifa_next; - } - freeifaddrs(ifaddr); -} - int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) const { struct sockaddr_nl sa = {}; sa.nl_family = AF_NETLINK; @@ -635,7 +589,8 @@ void waybar::modules::Network::checkNewInterface(struct ifinfomsg *rtif) { char ifname[IF_NAMESIZE]; if_indextoname(new_iface, ifname); ifname_ = ifname; - getInterfaceAddress(); + want_addr_dump_ = true; + askForStateDump(); thread_timer_.wake_up(); } else { ifid_ = -1; @@ -647,7 +602,6 @@ 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)); bool is_del_event = false; switch (nh->nlmsg_type) { @@ -681,8 +635,12 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifname_ = new_ifname; net->ifid_ = ifi->ifi_index; - net->getInterfaceAddress(); net->thread_timer_.wake_up(); + /* An address for this new interface should be received via an + * RTM_NEWADDR event either because we ask for a dump of both links + * and addrs, or because this interface has just been created and + * the addr will be sent after the RTM_NEWLINK event. + * So we don't need to do anything. */ } } else if (is_del_event && net->ifid_ >= 0) { // Our interface has been deleted, start looking/waiting for one we care. @@ -693,46 +651,78 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->checkNewInterface(ifi); net->dp.emit(); } - return NL_OK; + break; + } + + case RTM_DELADDR: + is_del_event = true; + case RTM_NEWADDR: { + struct ifaddrmsg *ifa = static_cast(NLMSG_DATA(nh)); + ssize_t attrlen = IFA_PAYLOAD(nh); + struct rtattr *ifa_rta = IFA_RTA(ifa); + + if ((int)ifa->ifa_index != net->ifid_) { + return NL_OK; + } + + if (ifa->ifa_family != net->family_) { + return NL_OK; + } + + // We ignore address mark as scope for the link or host, + // which should leave scope global addresses. + if (ifa->ifa_scope >= RT_SCOPE_LINK) { + return NL_OK; + } + + for (; RTA_OK(ifa_rta, attrlen); ifa_rta = RTA_NEXT(ifa_rta, attrlen)) { + switch (ifa_rta->rta_type) { + case IFA_ADDRESS: { + char ipaddr[INET6_ADDRSTRLEN]; + if (!is_del_event) { + net->ipaddr_ = inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), + ipaddr, sizeof (ipaddr)); + net->cidr_ = ifa->ifa_prefixlen; + switch (ifa->ifa_family) { + case AF_INET: { + struct in_addr netmask; + netmask.s_addr = htonl(~0 << (32 - ifa->ifa_prefixlen)); + net->netmask_ = inet_ntop(ifa->ifa_family, &netmask, + ipaddr, sizeof (ipaddr)); + } + case AF_INET6: { + struct in6_addr netmask; + for (int i = 0; i < 16; i++) { + int v = (i + 1) * 8 - ifa->ifa_prefixlen; + if (v < 0) v = 0; + if (v > 8) v = 8; + netmask.s6_addr[i] = ~0 << v; + } + net->netmask_ = inet_ntop(ifa->ifa_family, &netmask, + ipaddr, sizeof (ipaddr)); + } + } + spdlog::debug("network: {}, new addr {}/{}", net->ifname_, net->ipaddr_, net->cidr_); + } else { + net->ipaddr_.clear(); + net->cidr_ = 0; + net->netmask_.clear(); + spdlog::debug("network: {} addr deleted {}/{}", + net->ifname_, + inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), + ipaddr, sizeof (ipaddr)), + ifa->ifa_prefixlen); + } + net->dp.emit(); + break; + } + } + } + break; } } - 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 { - 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; + return NL_OK; } void waybar::modules::Network::askForStateDump(void) { @@ -751,6 +741,12 @@ void waybar::modules::Network::askForStateDump(void) { want_link_dump_ = false; dump_in_progress_ = true; + } else if (want_addr_dump_) { + rt_hdr.rtgen_family = family_; + nl_send_simple(ev_sock_, RTM_GETADDR, NLM_F_DUMP, + &rt_hdr, sizeof (rt_hdr)); + want_addr_dump_ = false; + dump_in_progress_ = true; } } From 0bb436f9493424d63e66a39e2dc2f5e6e2ada9a3 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 232/355] network: Rework interface auto detection, handle route change events Last part of the rework of handleEvents(), this time we take the getExternalInterface() function and add it to the handleEvents() function. That way, waybar can react immediately when a new "external interface" is available and doesn't need to probe. Also that avoid to have two different functions consuming from the same socket and we don't need to recode some of the functions that are already available via libnl (to send and receive messages). --- include/modules/network.hpp | 9 +- src/modules/network.cpp | 356 +++++++++++++++--------------------- 2 files changed, 146 insertions(+), 219 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index cc8b3f5..964597a 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -35,17 +34,12 @@ class Network : public ALabel { void worker(); void createInfoSocket(); void createEventSocket(); - int getExternalInterface(int skip_idx = -1) const; - 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; + bool checkInterface(std::string name); 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; @@ -60,6 +54,7 @@ class Network : public ALabel { int nl80211_id_; std::mutex mutex_; + bool want_route_dump_; bool want_link_dump_; bool want_addr_dump_; bool dump_in_progress_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index f4cee78..508af91 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -86,6 +86,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), ev_fd_(-1), + want_route_dump_(false), want_link_dump_(false), want_addr_dump_(false), dump_in_progress_(false), @@ -117,7 +118,11 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf bandwidth_up_total_ = 0; } - if (config_["interface"].isString()) { + if (!config_["interface"].isString()) { + // "interface" isn't configure, then try to guess the external + // interface currently used for internet. + want_route_dump_ = true; + } else { // Look for an interface that match "interface" // and then find the address associated with it. want_link_dump_ = true; @@ -126,14 +131,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf 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; - want_addr_dump_ = true; - } + dp.emit(); // Ask for a dump of interfaces and then addresses to populate our // information. First the interface dump, and once done, the callback @@ -184,6 +182,14 @@ void waybar::modules::Network::createEventSocket() { } else { nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); } + if (!config_["interface"].isString()) { + if (family_ == AF_INET) { + nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_ROUTE); + } else { + nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_ROUTE); + } + } + efd_ = epoll_create1(EPOLL_CLOEXEC); if (efd_ < 0) { throw std::runtime_error("Can't create epoll"); @@ -383,196 +389,16 @@ auto waybar::modules::Network::update() -> void { ALabel::update(); } -// 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; -} - -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) { +bool waybar::modules::Network::checkInterface(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()) { - // Using link dump instead - return -1; - } - // 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() { + ifid_ = -1; essid_.clear(); ipaddr_.clear(); netmask_.clear(); @@ -582,22 +408,6 @@ void waybar::modules::Network::clearIface() { 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; - want_addr_dump_ = true; - askForStateDump(); - 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_); @@ -627,10 +437,16 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } } - if (!is_del_event && net->ifid_ == -1) { + if (!is_del_event && ifi->ifi_index == net->ifid_) { + // Update inferface information + if (net->ifname_.empty()) { + std::string new_ifname (ifname, ifname_len); + net->ifname_ = new_ifname; + } + } else if (!is_del_event && net->ifid_ == -1) { // Checking if it's an interface we care about. std::string new_ifname (ifname, ifname_len); - if (net->checkInterface(ifi, new_ifname)) { + if (net->checkInterface(new_ifname)) { spdlog::debug("network: selecting new interface {}/{}", new_ifname, ifi->ifi_index); net->ifname_ = new_ifname; @@ -647,8 +463,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { spdlog::debug("network: interface {}/{} deleted", net->ifname_, net->ifid_); net->clearIface(); - // Check for a new interface and get info - net->checkNewInterface(ifi); net->dp.emit(); } break; @@ -720,6 +534,117 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } break; } + + case RTM_DELROUTE: + is_del_event = true; + case RTM_NEWROUTE: { + // Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698 + // to find the interface used to reach the outside world + + struct rtmsg *rtm = static_cast(NLMSG_DATA(nh)); + ssize_t attrlen = RTM_PAYLOAD(nh); + struct rtattr *attr = RTM_RTA(rtm); + bool has_gateway = false; + bool has_destination = false; + int temp_idx = -1; + + /* If we found the correct answer, skip parsing the attributes. */ + if (!is_del_event && net->ifid_ != -1) { + return NL_OK; + } + + /* Find the message(s) concerting the main routing table, each message + * corresponds to a single routing table entry. + */ + if (rtm->rtm_table != RT_TABLE_MAIN) { + return NL_OK; + } + + /* Parse all the attributes for a single routing table entry. */ + for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) { + /* Determine if this routing table entry corresponds to the default + * route by seeing if it has a gateway, and if a destination addr is + * set, that it is all 0s. + */ + switch(attr->rta_type) { + case RTA_GATEWAY: + /* The gateway of the route. + * + * If someone every needs to figure out the gateway address as well, + * it's here as the attribute payload. + */ + has_gateway = true; + break; + case RTA_DST: { + /* The destination address. + * Should be either missing, or maybe all 0s. Accept both. + */ + const uint32_t nr_zeroes = (net->family_ == AF_INET) ? 4 : 16; + unsigned char c = 0; + size_t dstlen = RTA_PAYLOAD(attr); + if (dstlen != nr_zeroes) { + break; + } + for (uint32_t i = 0; i < dstlen; i += 1) { + c |= *((unsigned char *)RTA_DATA(attr) + i); + } + has_destination = (c == 0); + break; + } + case RTA_OIF: + /* The output interface index. */ + temp_idx = *static_cast(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) { + if (!is_del_event) { + net->ifid_ = temp_idx; + + spdlog::debug("network: new default route via if{}", temp_idx); + + /* Ask ifname associated with temp_idx as well as carrier status */ + struct ifinfomsg ifinfo_hdr = { + .ifi_family = AF_UNSPEC, + .ifi_index = temp_idx, + }; + int err; + err = nl_send_simple(net->ev_sock_, RTM_GETLINK, NLM_F_REQUEST, + &ifinfo_hdr, sizeof (ifinfo_hdr)); + if (err < 0) { + spdlog::error("network: failed to ask link info: {}", err); + /* Ask for a dump of all links instead */ + net->want_link_dump_ = true; + } + + /* Also ask for the address. Asking for a addresses of a specific + * interface doesn't seems to work so ask for a dump of all + * addresses. */ + net->want_addr_dump_ = true; + net->askForStateDump(); + net->thread_timer_.wake_up(); + } else if (is_del_event && temp_idx == net->ifid_) { + spdlog::debug("network: default route deleted {}/if{}", + net->ifname_, temp_idx); + + net->ifname_.clear(); + net->clearIface(); + net->dp.emit(); + /* Ask for a dump of all routes in case another one is already + * setup. If there's none, there'll be an event with new one + * later. */ + net->want_route_dump_ = true; + net->askForStateDump(); + } + } + } + break; + } } return NL_OK; @@ -735,7 +660,14 @@ void waybar::modules::Network::askForStateDump(void) { .rtgen_family = AF_UNSPEC, }; - if (want_link_dump_) { + if (want_route_dump_) { + rt_hdr.rtgen_family = family_; + nl_send_simple(ev_sock_, RTM_GETROUTE, NLM_F_DUMP, + &rt_hdr, sizeof (rt_hdr)); + want_route_dump_ = false; + dump_in_progress_ = true; + + } else if (want_link_dump_) { nl_send_simple(ev_sock_, RTM_GETLINK, NLM_F_DUMP, &rt_hdr, sizeof (rt_hdr)); want_link_dump_ = false; From c1427ff8072aa9573a09941fbaa1c45af5005d89 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 15 May 2021 16:38:00 +0100 Subject: [PATCH 233/355] network: Handle carrier information IFLA_CARRIER allows to know when a cable is plugged to the Ethernet card or when the WiFi is connected. If there's no carrier, the interface will be considered disconnected. --- include/modules/network.hpp | 1 + src/modules/network.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 964597a..2523dc2 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -64,6 +64,7 @@ class Network : public ALabel { std::string state_; std::string essid_; + bool carrier_; std::string ifname_; std::string ipaddr_; std::string netmask_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 508af91..6102a43 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -292,6 +292,7 @@ const std::string waybar::modules::Network::getNetworkState() const { #endif return "disconnected"; } + if (!carrier_) return "disconnected"; if (ipaddr_.empty()) return "linked"; if (essid_.empty()) return "ethernet"; return "wifi"; @@ -402,6 +403,7 @@ void waybar::modules::Network::clearIface() { essid_.clear(); ipaddr_.clear(); netmask_.clear(); + carrier_ = false; cidr_ = 0; signal_strength_dbm_ = 0; signal_strength_ = 0; @@ -423,6 +425,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { struct rtattr *ifla = IFLA_RTA(ifi); const char *ifname = NULL; size_t ifname_len = 0; + bool carrier = false; if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) { return NL_OK; @@ -434,6 +437,10 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { ifname = static_cast(RTA_DATA(ifla)); ifname_len = RTA_PAYLOAD(ifla) - 1; // minus \0 break; + case IFLA_CARRIER: { + carrier = *(char*)RTA_DATA(ifla) == 1; + break; + } } } @@ -443,6 +450,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { std::string new_ifname (ifname, ifname_len); net->ifname_ = new_ifname; } + net->carrier_ = carrier; } else if (!is_del_event && net->ifid_ == -1) { // Checking if it's an interface we care about. std::string new_ifname (ifname, ifname_len); @@ -451,6 +459,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifname_ = new_ifname; net->ifid_ = ifi->ifi_index; + net->carrier_ = carrier; net->thread_timer_.wake_up(); /* An address for this new interface should be received via an * RTM_NEWADDR event either because we ask for a dump of both links From c65ec9e14fa3509c2da67551e14add961f9850ce Mon Sep 17 00:00:00 2001 From: Yonatan Avhar Date: Fri, 21 May 2021 15:54:48 +0300 Subject: [PATCH 234/355] Add options to use a .json extension for the config filename --- src/client.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index 1c48c81..f6330c3 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -207,9 +207,13 @@ 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", + "$XDG_CONFIG_HOME/waybar/config.json", "$HOME/.config/waybar/config", + "$HOME/.config/waybar/config.json", "$HOME/waybar/config", + "$HOME/waybar/config.json", "/etc/xdg/waybar/config", + "/etc/xdg/waybar/config.json", SYSCONFDIR "/xdg/waybar/config", "./resources/config", }) From 99918205ede41e80fc5e55e0b2582ae3fc613e21 Mon Sep 17 00:00:00 2001 From: Yonatan Avhar Date: Fri, 21 May 2021 17:53:43 +0300 Subject: [PATCH 235/355] Correct .json to .jsonc --- src/client.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index f6330c3..ced9e49 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -207,13 +207,13 @@ 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", - "$XDG_CONFIG_HOME/waybar/config.json", + "$XDG_CONFIG_HOME/waybar/config.jsonc", "$HOME/.config/waybar/config", - "$HOME/.config/waybar/config.json", + "$HOME/.config/waybar/config.jsonc", "$HOME/waybar/config", - "$HOME/waybar/config.json", + "$HOME/waybar/config.jsonc", "/etc/xdg/waybar/config", - "/etc/xdg/waybar/config.json", + "/etc/xdg/waybar/config.jsonc", SYSCONFDIR "/xdg/waybar/config", "./resources/config", }) From 729553d3bc8eeb3c7a5370692174d9962d242e9b Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 17 Jan 2021 01:05:18 -0800 Subject: [PATCH 236/355] feat(bar): add config flag for pointer event passthrough --- include/bar.hpp | 1 + src/bar.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/include/bar.hpp b/include/bar.hpp index d6cd895..6bf8c52 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -44,6 +44,7 @@ class BarSurface { virtual void setExclusiveZone(bool enable) = 0; virtual void setLayer(bar_layer layer) = 0; virtual void setMargins(const struct bar_margins &margins) = 0; + virtual void setPassThrough(bool enable) = 0; virtual void setPosition(const std::string_view &position) = 0; virtual void setSize(uint32_t width, uint32_t height) = 0; virtual void commit(){}; diff --git a/src/bar.cpp b/src/bar.cpp index 1dbd69a..3361ebf 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -33,6 +33,7 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { gtk_layer_set_monitor(window_.gobj(), output.monitor->gobj()); gtk_layer_set_namespace(window_.gobj(), "waybar"); + window.signal_map_event().connect_notify(sigc::mem_fun(*this, &GLSSurfaceImpl::onMap)); window.signal_configure_event().connect_notify( sigc::mem_fun(*this, &GLSSurfaceImpl::onConfigure)); } @@ -62,6 +63,18 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { gtk_layer_set_layer(window_.gobj(), layer); } + void setPassThrough(bool enable) override { + passthrough_ = enable; + auto gdk_window = window_.get_window(); + if (gdk_window) { + Cairo::RefPtr region; + if (enable) { + region = Cairo::Region::create(); + } + gdk_window->input_shape_combine_region(region, 0, 0); + } + } + void setPosition(const std::string_view& position) override { auto unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM; vertical_ = false; @@ -93,8 +106,11 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { std::string output_name_; uint32_t width_; uint32_t height_; + bool passthrough_ = false; bool vertical_ = false; + void onMap(GdkEventAny* ev) { setPassThrough(passthrough_); } + void onConfigure(GdkEventConfigure* ev) { /* * GTK wants new size for the window. @@ -182,6 +198,20 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } } + void setPassThrough(bool enable) override { + passthrough_ = enable; + /* GTK overwrites any region changes applied directly to the wl_surface, + * thus the same GTK region API as in the GLS impl has to be used. */ + auto gdk_window = window_.get_window(); + if (gdk_window) { + Cairo::RefPtr region; + if (enable) { + region = Cairo::Region::create(); + } + gdk_window->input_shape_combine_region(region, 0, 0); + } + } + void setPosition(const std::string_view& position) override { anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; if (position == "bottom") { @@ -230,6 +260,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { uint32_t height_ = 0; uint8_t anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; bool exclusive_zone_ = true; + bool passthrough_ = false; struct bar_margins margins_; zwlr_layer_shell_v1_layer layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; @@ -262,6 +293,7 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { setSurfaceSize(width_, height_); setExclusiveZone(exclusive_zone_); + setPassThrough(passthrough_); commit(); wl_display_roundtrip(client->wl_display); @@ -377,6 +409,14 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) layer_ = bar_layer::OVERLAY; } + bool passthrough = false; + if (config["passthrough"].isBool()) { + passthrough = config["passthrough"].asBool(); + } else if (layer_ == bar_layer::OVERLAY) { + // swaybar defaults: overlay mode does not accept pointer events. + passthrough = true; + } + auto position = config["position"].asString(); if (position == "right" || position == "left") { @@ -386,7 +426,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) box_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); vertical = true; } - + left_.get_style_context()->add_class("modules-left"); center_.get_style_context()->add_class("modules-center"); right_.get_style_context()->add_class("modules-right"); @@ -454,6 +494,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) surface_impl_->setLayer(layer_); surface_impl_->setExclusiveZone(true); surface_impl_->setMargins(margins_); + surface_impl_->setPassThrough(passthrough); surface_impl_->setPosition(position); surface_impl_->setSize(width, height); From 7aaa3df701cdf660c915efec5ee3145180dac9c7 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 17 Jan 2021 01:40:25 -0800 Subject: [PATCH 237/355] feat(bar): add config flag to disable exclusive zone --- include/bar.hpp | 1 + src/bar.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 6bf8c52..6f3dfcf 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -65,6 +65,7 @@ class Bar { struct waybar_output *output; Json::Value config; struct wl_surface * surface; + bool exclusive = true; bool visible = true; bool vertical = false; Gtk::Window window; diff --git a/src/bar.cpp b/src/bar.cpp index 3361ebf..7d76359 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -409,6 +409,13 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) layer_ = bar_layer::OVERLAY; } + if (config["exclusive"].isBool()) { + exclusive = config["exclusive"].asBool(); + } else if (layer_ == bar_layer::OVERLAY) { + // swaybar defaults: overlay mode does not reserve an exclusive zone + exclusive = false; + } + bool passthrough = false; if (config["passthrough"].isBool()) { passthrough = config["passthrough"].asBool(); @@ -492,7 +499,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) } surface_impl_->setLayer(layer_); - surface_impl_->setExclusiveZone(true); + surface_impl_->setExclusiveZone(exclusive); surface_impl_->setMargins(margins_); surface_impl_->setPassThrough(passthrough); surface_impl_->setPosition(position); @@ -533,7 +540,7 @@ void waybar::Bar::setVisible(bool value) { window.set_opacity(1); surface_impl_->setLayer(layer_); } - surface_impl_->setExclusiveZone(visible); + surface_impl_->setExclusiveZone(exclusive && visible); surface_impl_->commit(); } From da2d603b53d42f68c41055731bd3a4a50dc11f9b Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 21 May 2021 22:38:48 -0700 Subject: [PATCH 238/355] doc: add man for exclusive and passthrough flags --- man/waybar.5.scd.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 430b9fc..fe11c4a 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -68,6 +68,17 @@ Also a minimal example configuration can be found on the at the bottom of this m typeof: string ++ Optional name added as a CSS class, for styling multiple waybars. +*exclusive* ++ + typeof: bool ++ + default: *true* unless the layer is set to *overlay* ++ + Option to request an exclusive zone from the compositor. Disable this to allow drawing application windows underneath or on top of the bar. + +*passthrough* ++ + typeof: bool ++ + default: *false* unless the layer is set to *overlay* ++ + Option to pass any pointer events to the window under the bar. + Intended to be used with either *top* or *overlay* layers and without exclusive zone. + *gtk-layer-shell* ++ typeof: bool ++ default: true ++ From 28dfb0ba41f2d0f97b2f84de77e878c6c24fc29d Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 26 May 2021 18:28:49 +0100 Subject: [PATCH 239/355] network: Fix use of carrier information Some RTM_NEWLINK messages may not have the IFLA_CARRIER information. This is the case when a WiFi interface report scan result are available. `carrier` is used regardless of if it is present in the message or not. This would result in the interface appearing "disconnected" in waybar when it isn't. This patch now check that `carrier` is available before using it. The same thing could potentially happen to `ifname` so check if it's set before recording it. Fixes: c1427ff (network: Handle carrier information) Fixes #388 --- src/modules/network.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 6102a43..9e0ed39 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -425,7 +425,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { struct rtattr *ifla = IFLA_RTA(ifi); const char *ifname = NULL; size_t ifname_len = 0; - bool carrier = false; + std::optional carrier; if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) { return NL_OK; @@ -446,11 +446,13 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { if (!is_del_event && ifi->ifi_index == net->ifid_) { // Update inferface information - if (net->ifname_.empty()) { + if (net->ifname_.empty() && ifname != NULL) { std::string new_ifname (ifname, ifname_len); net->ifname_ = new_ifname; } - net->carrier_ = carrier; + if (carrier.has_value()) { + net->carrier_ = carrier.value(); + } } else if (!is_del_event && net->ifid_ == -1) { // Checking if it's an interface we care about. std::string new_ifname (ifname, ifname_len); @@ -459,7 +461,9 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifname_ = new_ifname; net->ifid_ = ifi->ifi_index; - net->carrier_ = carrier; + if (carrier.has_value()) { + net->carrier_ = carrier.value(); + } net->thread_timer_.wake_up(); /* An address for this new interface should be received via an * RTM_NEWADDR event either because we ask for a dump of both links From f49a7a1acbe8489f37a88f5c8f7941a44bb90ac7 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 26 May 2021 18:51:02 +0100 Subject: [PATCH 240/355] network: Update WiFi information when available The module doesn't update the `essid_` as soon as a WiFi interface is connected, but that happens at some point later, depending on "interval" configuration. Fix that by rerunning the get WiFi information thread when the `carrier` state changes. Also, we will clear the state related to WiFi when the connection is drop to avoid stale information. --- src/modules/network.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 9e0ed39..583daae 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -451,6 +451,18 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifname_ = new_ifname; } if (carrier.has_value()) { + if (net->carrier_ != *carrier) { + if (*carrier) { + // Ask for WiFi information + net->thread_timer_.wake_up(); + } else { + // clear state related to WiFi connection + net->essid_.clear(); + net->signal_strength_dbm_ = 0; + net->signal_strength_ = 0; + net->frequency_ = 0; + } + } net->carrier_ = carrier.value(); } } else if (!is_del_event && net->ifid_ == -1) { From 999c1b6b81c5fe29c32e1e4450d49d67d0e6570c Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Sat, 5 Jun 2021 14:50:25 +0200 Subject: [PATCH 241/355] sway-language: ignore events with empty layout --- src/modules/sway/language.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index a318647..4d27fb8 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -45,7 +45,9 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { auto payload = parser_.parse(res.payload)["input"]; if (payload["type"].asString() == "keyboard") { auto layout_name = payload["xkb_active_layout_name"].asString().substr(0,2); - lang_ = Glib::Markup::escape_text(layout_name); + if (!layout_name.empty()) { + lang_ = Glib::Markup::escape_text(layout_name); + } } dp.emit(); } catch (const std::exception& e) { From 23b9923eeba13bdba51b601f2c5c7e4f20b8710a Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sun, 30 May 2021 15:56:56 +0100 Subject: [PATCH 242/355] network: Parse whole RTM_NEWROUTE msg before interpreting it The check to figure out if we have the default route should be after the for loop that parses the route attributes, to avoid acting on incomplete information. We are going to use more fields from the message. --- src/modules/network.cpp | 76 ++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 583daae..3edc4dc 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -623,49 +623,47 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { 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) { - if (!is_del_event) { - net->ifid_ = temp_idx; + // Check if we have a default route. + if (has_gateway && !has_destination && temp_idx != -1) { + if (!is_del_event) { + net->ifid_ = temp_idx; - spdlog::debug("network: new default route via if{}", temp_idx); + spdlog::debug("network: new default route via if{}", temp_idx); - /* Ask ifname associated with temp_idx as well as carrier status */ - struct ifinfomsg ifinfo_hdr = { - .ifi_family = AF_UNSPEC, - .ifi_index = temp_idx, - }; - int err; - err = nl_send_simple(net->ev_sock_, RTM_GETLINK, NLM_F_REQUEST, - &ifinfo_hdr, sizeof (ifinfo_hdr)); - if (err < 0) { - spdlog::error("network: failed to ask link info: {}", err); - /* Ask for a dump of all links instead */ - net->want_link_dump_ = true; - } - - /* Also ask for the address. Asking for a addresses of a specific - * interface doesn't seems to work so ask for a dump of all - * addresses. */ - net->want_addr_dump_ = true; - net->askForStateDump(); - net->thread_timer_.wake_up(); - } else if (is_del_event && temp_idx == net->ifid_) { - spdlog::debug("network: default route deleted {}/if{}", - net->ifname_, temp_idx); - - net->ifname_.clear(); - net->clearIface(); - net->dp.emit(); - /* Ask for a dump of all routes in case another one is already - * setup. If there's none, there'll be an event with new one - * later. */ - net->want_route_dump_ = true; - net->askForStateDump(); + /* Ask ifname associated with temp_idx as well as carrier status */ + struct ifinfomsg ifinfo_hdr = { + .ifi_family = AF_UNSPEC, + .ifi_index = temp_idx, + }; + int err; + err = nl_send_simple(net->ev_sock_, RTM_GETLINK, NLM_F_REQUEST, + &ifinfo_hdr, sizeof (ifinfo_hdr)); + if (err < 0) { + spdlog::error("network: failed to ask link info: {}", err); + /* Ask for a dump of all links instead */ + net->want_link_dump_ = true; } + + /* Also ask for the address. Asking for a addresses of a specific + * interface doesn't seems to work so ask for a dump of all + * addresses. */ + net->want_addr_dump_ = true; + net->askForStateDump(); + net->thread_timer_.wake_up(); + } else if (is_del_event && temp_idx == net->ifid_) { + spdlog::debug("network: default route deleted {}/if{}", + net->ifname_, temp_idx); + + net->ifname_.clear(); + net->clearIface(); + net->dp.emit(); + /* Ask for a dump of all routes in case another one is already + * setup. If there's none, there'll be an event with new one + * later. */ + net->want_route_dump_ = true; + net->askForStateDump(); } } break; From ce97df34e69689d97adbf481ef65997beb4f3f98 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 5 Jun 2021 15:44:32 +0100 Subject: [PATCH 243/355] network: Also clear ifname in clearIface() Since we reset `ifid_`, clear `ifname_` as well. --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 3edc4dc..e895569 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -400,6 +400,7 @@ bool waybar::modules::Network::checkInterface(std::string name) { void waybar::modules::Network::clearIface() { ifid_ = -1; + ifname_.clear(); essid_.clear(); ipaddr_.clear(); netmask_.clear(); @@ -656,7 +657,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { spdlog::debug("network: default route deleted {}/if{}", net->ifname_, temp_idx); - net->ifname_.clear(); net->clearIface(); net->dp.emit(); /* Ask for a dump of all routes in case another one is already From efaac20d8200b2249668773fa96ef40ea66fe4fe Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 2 Jun 2021 22:15:34 +0100 Subject: [PATCH 244/355] network: Handle ip route priority When there's a new default route with higher priority, switch to it. --- include/modules/network.hpp | 1 + src/modules/network.cpp | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 2523dc2..009ae5a 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -72,6 +72,7 @@ class Network : public ALabel { int32_t signal_strength_dbm_; uint8_t signal_strength_; uint32_t frequency_; + uint32_t route_priority; util::SleeperThread thread_; util::SleeperThread thread_timer_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index e895569..de023ef 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -573,11 +573,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { bool has_gateway = false; bool has_destination = false; int temp_idx = -1; - - /* If we found the correct answer, skip parsing the attributes. */ - if (!is_del_event && net->ifid_ != -1) { - return NL_OK; - } + uint32_t priority = 0; /* Find the message(s) concerting the main routing table, each message * corresponds to a single routing table entry. @@ -621,6 +617,9 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { /* The output interface index. */ temp_idx = *static_cast(RTA_DATA(attr)); break; + case RTA_PRIORITY: + priority = *(uint32_t*)RTA_DATA(attr); + break; default: break; } @@ -628,10 +627,16 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { // Check if we have a default route. if (has_gateway && !has_destination && temp_idx != -1) { - if (!is_del_event) { + // Check if this is the first default route we see, or if this new + // route have a higher priority. + if (!is_del_event && ((net->ifid_ == -1) || (priority < net->route_priority))) { + // Clear if's state for the case were there is a higher priority + // route on a different interface. + net->clearIface(); net->ifid_ = temp_idx; + net->route_priority = priority; - spdlog::debug("network: new default route via if{}", temp_idx); + spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority); /* Ask ifname associated with temp_idx as well as carrier status */ struct ifinfomsg ifinfo_hdr = { @@ -653,9 +658,10 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->want_addr_dump_ = true; net->askForStateDump(); net->thread_timer_.wake_up(); - } else if (is_del_event && temp_idx == net->ifid_) { - spdlog::debug("network: default route deleted {}/if{}", - net->ifname_, temp_idx); + } else if (is_del_event && temp_idx == net->ifid_ + && net->route_priority == priority) { + spdlog::debug("network: default route deleted {}/if{} metric {}", + net->ifname_, temp_idx, priority); net->clearIface(); net->dp.emit(); From 33617b67f0c342f15a72da413c4e6b0b46f9e196 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 2 Jun 2021 22:16:28 +0100 Subject: [PATCH 245/355] network: Fix one case where default route is deleted without notification When an interface's state is change to "down", all the route associated with it are deleted without an RTM_DELROUTE event. So when this happen, reset the module to start looking for a new external interface / default route. Fixes #1117 --- src/modules/network.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index de023ef..ec221a2 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -1,6 +1,7 @@ #include "modules/network.hpp" #include #include +#include #include #include #include @@ -432,6 +433,20 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { return NL_OK; } + // Check if the interface goes "down" and if we want to detect the + // external interface. + if (net->ifid_ != -1 && !(ifi->ifi_flags & IFF_UP) + && !net->config_["interface"].isString()) { + // The current interface is now down, all the routes associated with + // it have been deleted, so start looking for a new default route. + spdlog::debug("network: if{} down", net->ifid_); + net->clearIface(); + net->dp.emit(); + net->want_route_dump_ = true; + net->askForStateDump(); + return NL_OK; + } + for (; RTA_OK(ifla, attrlen); ifla = RTA_NEXT(ifla, attrlen)) { switch (ifla->rta_type) { case IFLA_IFNAME: From 194f4c2f18799633a1877fb79ea82c47f4aa30c7 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Mon, 7 Jun 2021 19:24:38 +0100 Subject: [PATCH 246/355] network: Fix mix use of default and state specific format Whenever the network module is configured with both "format" and "format-$state" and when the module use "format-$state" once, it override the value that was saved from "format". For example, if both "format" and "format-disconnect" are configured, and only those, as soon as the module show information about a disconnected interface, it will keep showing the format for disconnected, even if the interface is connected again later. Fix that by always setting a value to default_format_ in update() and thus use the intended default format when needed. Fixes #1129 --- src/modules/network.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index ec221a2..7d0f638 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -19,6 +19,7 @@ constexpr const char *NETSTAT_FILE = 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"; +constexpr const char *DEFAULT_FORMAT = "{ifname}"; std::ifstream netstat(NETSTAT_FILE); std::optional read_netstat(std::string_view category, std::string_view key) { @@ -82,7 +83,7 @@ std::optional read_netstat(std::string_view category, std::s } // namespace waybar::modules::Network::Network(const std::string &id, const Json::Value &config) - : ALabel(config, "network", id, "{ifname}", 60), + : ALabel(config, "network", id, DEFAULT_FORMAT, 60), ifid_(-1), family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), @@ -323,6 +324,10 @@ auto waybar::modules::Network::update() -> void { } if (config_["format-" + state].isString()) { default_format_ = config_["format-" + state].asString(); + } else if (config_["format"].isString()) { + default_format_ = config_["format"].asString(); + } else { + default_format_ = DEFAULT_FORMAT; } if (config_["tooltip-format-" + state].isString()) { tooltip_format = config_["tooltip-format-" + state].asString(); From 5da268077cb0e53a5ff46fe9ca2dee8bf8e1c041 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 4 Jun 2021 20:22:16 -0700 Subject: [PATCH 247/355] fix(util): protect std::condition_variable methods from pthread_cancel The changes in GCC 11.x made `std::condition_variable` implementation internals `noexcept`. `noexcept` is known to interact particularly bad with `pthread_cancel`, i.e. `__cxxabiv1::__force_unwind` passing through the `noexcept` call stack frame causes a `std::terminate` call and immediate termination of the program Digging through the GCC ML archives[1] lead me to the idea of patching this with a few pthread_setcancelstate's. As bad as the solution is, it seems to be the best we can do within C++17 limits and without major rework. [1]: https://gcc.gnu.org/legacy-ml/gcc/2017-08/msg00156.html --- include/util/sleeper_thread.hpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/include/util/sleeper_thread.hpp b/include/util/sleeper_thread.hpp index 9adbe8f..d1c6ba0 100644 --- a/include/util/sleeper_thread.hpp +++ b/include/util/sleeper_thread.hpp @@ -8,6 +8,20 @@ namespace waybar::util { +/** + * Defer pthread_cancel until the end of a current scope. + * + * Required to protect a scope where it's unsafe to raise `__forced_unwind` exception. + * An example of these is a call of a method marked as `noexcept`; an attempt to cancel within such + * a method may result in a `std::terminate` call. + */ +class CancellationGuard { + int oldstate; +public: + CancellationGuard() { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); } + ~CancellationGuard() { pthread_setcancelstate(oldstate, &oldstate); } +}; + class SleeperThread { public: SleeperThread() = default; @@ -33,14 +47,16 @@ class SleeperThread { bool isRunning() const { return do_run_; } auto sleep_for(std::chrono::system_clock::duration dur) { - std::unique_lock lk(mutex_); + std::unique_lock lk(mutex_); + CancellationGuard cancel_lock; return condvar_.wait_for(lk, dur, [this] { return signal_ || !do_run_; }); } auto sleep_until( std::chrono::time_point time_point) { - std::unique_lock lk(mutex_); + std::unique_lock lk(mutex_); + CancellationGuard cancel_lock; return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; }); } From 14f626d422a77b51bca4bbbe5282ee6e995169e0 Mon Sep 17 00:00:00 2001 From: Oskar Carl Date: Sun, 24 Jan 2021 17:36:30 +0100 Subject: [PATCH 248/355] Add recursive config includes --- include/client.hpp | 1 + src/client.cpp | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/client.hpp b/include/client.hpp index ec3866a..af6eeef 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -39,6 +39,7 @@ class Client { void handleOutput(struct waybar_output &output); bool isValidOutput(const Json::Value &config, struct waybar_output &output); auto setupConfig(const std::string &config_file) -> void; + auto mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void; auto setupCss(const std::string &css_file) -> void; struct waybar_output & getOutput(void *); std::vector getOutputConfigs(struct waybar_output &output); diff --git a/src/client.cpp b/src/client.cpp index ced9e49..752574d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -241,7 +241,24 @@ auto waybar::Client::setupConfig(const std::string &config_file) -> void { } std::string str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); util::JsonParser parser; - config_ = parser.parse(str); + Json::Value tmp_config_ = parser.parse(str); + if (tmp_config_["include"].isArray()) { + for (const auto& include : tmp_config_["include"]) { + spdlog::info("Including resource file: {}", include.asString()); + setupConfig(getValidPath({include.asString()})); + } + } + mergeConfig(config_, tmp_config_); +} + +auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { + for (const auto& key : b_config_.getMemberNames()) { + if (a_config_[key].type() == Json::objectValue && b_config_[key].type() == Json::objectValue) { + mergeConfig(a_config_[key], b_config_[key]); + } else { + a_config_[key] = b_config_[key]; + } + } } auto waybar::Client::setupCss(const std::string &css_file) -> void { From e8278431d281e6bfa2cae15c6874963711a1396b Mon Sep 17 00:00:00 2001 From: Oskar Carl Date: Sun, 24 Jan 2021 18:32:05 +0100 Subject: [PATCH 249/355] Proper formatting --- src/client.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 752574d..7f3229c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -241,9 +241,9 @@ auto waybar::Client::setupConfig(const std::string &config_file) -> void { } std::string str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); util::JsonParser parser; - Json::Value tmp_config_ = parser.parse(str); + Json::Value tmp_config_ = parser.parse(str); if (tmp_config_["include"].isArray()) { - for (const auto& include : tmp_config_["include"]) { + for (const auto &include : tmp_config_["include"]) { spdlog::info("Including resource file: {}", include.asString()); setupConfig(getValidPath({include.asString()})); } @@ -252,7 +252,7 @@ auto waybar::Client::setupConfig(const std::string &config_file) -> void { } auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { - for (const auto& key : b_config_.getMemberNames()) { + for (const auto &key : b_config_.getMemberNames()) { if (a_config_[key].type() == Json::objectValue && b_config_[key].type() == Json::objectValue) { mergeConfig(a_config_[key], b_config_[key]); } else { From e62b634f72a9b91a6f2e94ba04a7ac60bcee23cb Mon Sep 17 00:00:00 2001 From: Oskar Carl Date: Mon, 21 Jun 2021 19:29:09 +0200 Subject: [PATCH 250/355] Workaround for circular imports --- include/client.hpp | 2 +- src/client.cpp | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index af6eeef..a54bf4b 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -38,7 +38,7 @@ class Client { const std::string getValidPath(const std::vector &paths) const; void handleOutput(struct waybar_output &output); bool isValidOutput(const Json::Value &config, struct waybar_output &output); - auto setupConfig(const std::string &config_file) -> void; + auto setupConfig(const std::string &config_file, int depth) -> void; auto mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void; auto setupCss(const std::string &css_file) -> void; struct waybar_output & getOutput(void *); diff --git a/src/client.cpp b/src/client.cpp index 7f3229c..07c0b23 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -234,7 +234,10 @@ std::tuple waybar::Client::getConfigs( return {config_file, css_file}; } -auto waybar::Client::setupConfig(const std::string &config_file) -> void { +auto waybar::Client::setupConfig(const std::string &config_file, int depth) -> void { + if (depth > 100) { + throw std::runtime_error("Aborting due to likely recursive include in config files"); + } std::ifstream file(config_file); if (!file.is_open()) { throw std::runtime_error("Can't open config file"); @@ -245,7 +248,7 @@ auto waybar::Client::setupConfig(const std::string &config_file) -> void { if (tmp_config_["include"].isArray()) { for (const auto &include : tmp_config_["include"]) { spdlog::info("Including resource file: {}", include.asString()); - setupConfig(getValidPath({include.asString()})); + setupConfig(getValidPath({include.asString()}), ++depth); } } mergeConfig(config_, tmp_config_); @@ -337,7 +340,7 @@ int waybar::Client::main(int argc, char *argv[]) { } wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj()); auto [config_file, css_file] = getConfigs(config, style); - setupConfig(config_file); + setupConfig(config_file, 0); setupCss(css_file); bindInterfaces(); gtk_app->hold(); From 982d571b2ea0577b70582e2830ab878eb8070ff3 Mon Sep 17 00:00:00 2001 From: Oskar Carl Date: Wed, 23 Jun 2021 23:08:47 +0200 Subject: [PATCH 251/355] Add include man section --- man/waybar.5.scd.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index fe11c4a..6e6dd13 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -85,6 +85,10 @@ Also a minimal example configuration can be found on the at the bottom of this m Option to disable the use of gtk-layer-shell for popups. Only functional if compiled with gtk-layer-shell support. +*include* ++ + typeof: array ++ + Paths to additional configuration files. In case of duplicate options, the including file's value takes precedence. Make sure to avoid circular imports. + # MODULE FORMAT You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat). From 368e4813de5356332d1167e8200cb5633e772ed6 Mon Sep 17 00:00:00 2001 From: John Helmert III Date: Tue, 29 Jun 2021 21:29:12 -0500 Subject: [PATCH 252/355] libfmt >=8.0.0 compatibility --- include/util/format.hpp | 4 ++++ src/modules/clock.cpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/include/util/format.hpp b/include/util/format.hpp index 288d8f0..543a100 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -35,7 +35,11 @@ namespace fmt { // 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. +#if FMT_VERSION < 80000 width = parse_nonnegative_int(it, end, ctx); +#else + width = detail::parse_nonnegative_int(it, end, -1); +#endif } return it; } diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 22bedc7..82c5701 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -196,6 +196,9 @@ template <> struct fmt::formatter : fmt::formatter { template auto format(const waybar_time& t, FormatContext& ctx) { +#if FMT_VERSION >= 80000 + auto& tm_format = specs; +#endif return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime)); } }; From d3c59c42ef9c3878e0086b0e95100a2f832aa088 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 30 Jun 2021 23:28:01 -0700 Subject: [PATCH 253/355] feat(ci): add GitHub CI jobs on Linux --- .github/workflows/linux.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/linux.yml diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..e550e20 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,25 @@ +name: linux + +on: [push, pull_request] + +jobs: + build: + strategy: + matrix: + distro: + - alpine + - archlinux + - debian + - fedora + - opensuse + + runs-on: ubuntu-latest + container: + image: alexays/waybar:${{ matrix.distro }} + + steps: + - uses: actions/checkout@v2 + - name: configure + run: meson -Dman-pages=enabled build + - name: build + run: ninja -C build From 2a52efa99a7a9604f9d28b0c1bec8e74643c19cf Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 30 Jun 2021 23:50:37 -0700 Subject: [PATCH 254/355] chore: update fedora dockerfile --- Dockerfiles/fedora | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index d75083c..77c77cb 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -1,7 +1,12 @@ # vim: ft=Dockerfile -FROM fedora:32 +FROM fedora:latest -RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel pugixml-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel scdoc -y && \ - dnf group install "C Development Tools and Libraries" -y && \ +RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ + 'pkgconfig(dbusmenu-gtk3-0.4)' 'pkgconfig(fmt)' 'pkgconfig(gdk-pixbuf-2.0)' \ + 'pkgconfig(gio-unix-2.0)' 'pkgconfig(gtk-layer-shell-0)' 'pkgconfig(gtkmm-3.0)' \ + 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ + 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ + 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ + 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' && \ dnf clean all -y From 5420a9104652ece306faefcfe41a6f2191f02295 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Thu, 1 Jul 2021 00:13:05 -0700 Subject: [PATCH 255/355] chore: update FreeBSD action to address ntp sync issue --- .github/workflows/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 7072c49..74422ee 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -8,7 +8,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Test in FreeBSD VM - uses: vmactions/freebsd-vm@v0.0.9 # aka FreeBSD 12.2 + uses: vmactions/freebsd-vm@v0.1.3 # aka FreeBSD 12.2 with: usesh: true prepare: | From 8e1f85e1c30b7431097e7441cfb80db5fccc029b Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 3 Jul 2021 00:27:57 +0200 Subject: [PATCH 256/355] Update archlinux --- Dockerfiles/archlinux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index 0b618ff..e5a53ef 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -1,6 +1,6 @@ # vim: ft=Dockerfile -FROM archlinux/base:latest +FROM archlinux:base-devel RUN pacman -Syu --noconfirm && \ pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm From a8edc0886d6524e3df46e58f98bc86184fa05bb1 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 3 Jul 2021 00:28:43 +0200 Subject: [PATCH 257/355] Delete .travis.yml --- .travis.yml | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index abc739c..0000000 --- a/.travis.yml +++ /dev/null @@ -1,38 +0,0 @@ -language: cpp - -services: - - docker - -git: - submodules: false - -env: - - distro: debian - - distro: archlinux - - distro: fedora - - distro: alpine - - distro: opensuse - -before_install: - - docker pull alexays/waybar:${distro} - - find . -type f \( -name '*.cpp' -o -name '*.h' \) -print0 | xargs -r0 clang-format -i - -script: - - echo FROM alexays/waybar:${distro} > Dockerfile - - echo ADD . /root >> Dockerfile - - docker build -t waybar . - - docker run waybar /bin/sh -c "cd /root && meson build -Dman-pages=enabled && ninja -C build" - -jobs: - include: - - os: freebsd - compiler: clang - env: - before_install: - - export CPPFLAGS+=-isystem/usr/local/include LDFLAGS+=-L/usr/local/lib # sndio - - sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf - - sudo pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu sndio - libfmt libmpdclient libudev-devd meson pulseaudio scdoc spdlog - script: - - meson build -Dman-pages=enabled - - ninja -C build From 569517c53164a8df6bfa2697bd4b7a97f44d0eb6 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 3 Jul 2021 00:55:59 +0200 Subject: [PATCH 258/355] chore: update freebsd-vm to 0.1.4 --- .github/workflows/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 74422ee..2fde610 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -8,7 +8,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Test in FreeBSD VM - uses: vmactions/freebsd-vm@v0.1.3 # aka FreeBSD 12.2 + uses: vmactions/freebsd-vm@v0.1.4 # aka FreeBSD 12.2 with: usesh: true prepare: | From 78aaa5c1b448dc3d744cd6e8cc1a5ee466774107 Mon Sep 17 00:00:00 2001 From: tiosgz Date: Sat, 10 Jul 2021 20:22:37 +0000 Subject: [PATCH 259/355] Do not fail to parse a multi-bar config --- include/client.hpp | 1 + man/waybar.5.scd.in | 3 ++- src/client.cpp | 48 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index a54bf4b..e7fa1db 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -39,6 +39,7 @@ class Client { void handleOutput(struct waybar_output &output); bool isValidOutput(const Json::Value &config, struct waybar_output &output); auto setupConfig(const std::string &config_file, int depth) -> void; + auto resolveConfigIncludes(Json::Value &config, int depth) -> void; auto mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void; auto setupCss(const std::string &css_file) -> void; struct waybar_output & getOutput(void *); diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 6e6dd13..0168de3 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -86,8 +86,9 @@ Also a minimal example configuration can be found on the at the bottom of this m Only functional if compiled with gtk-layer-shell support. *include* ++ - typeof: array ++ + typeof: string|array ++ Paths to additional configuration files. In case of duplicate options, the including file's value takes precedence. Make sure to avoid circular imports. + For a multi-bar config, specify at least an empty object for each bar also in every file being included. # MODULE FORMAT diff --git a/src/client.cpp b/src/client.cpp index 07c0b23..ff6e7bf 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -245,22 +245,50 @@ auto waybar::Client::setupConfig(const std::string &config_file, int depth) -> v std::string str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); util::JsonParser parser; Json::Value tmp_config_ = parser.parse(str); - if (tmp_config_["include"].isArray()) { - for (const auto &include : tmp_config_["include"]) { - spdlog::info("Including resource file: {}", include.asString()); - setupConfig(getValidPath({include.asString()}), ++depth); + if (tmp_config_.isArray()) { + for (auto &config_part : tmp_config_) { + resolveConfigIncludes(config_part, depth); } + } else { + resolveConfigIncludes(tmp_config_, depth); } mergeConfig(config_, tmp_config_); } -auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { - for (const auto &key : b_config_.getMemberNames()) { - if (a_config_[key].type() == Json::objectValue && b_config_[key].type() == Json::objectValue) { - mergeConfig(a_config_[key], b_config_[key]); - } else { - a_config_[key] = b_config_[key]; +auto waybar::Client::resolveConfigIncludes(Json::Value &config, int depth) -> void { + Json::Value includes = config["include"]; + if (includes.isArray()) { + for (const auto &include : includes) { + spdlog::info("Including resource file: {}", include.asString()); + setupConfig(getValidPath({include.asString()}), ++depth); } + } else if (includes.isString()) { + spdlog::info("Including resource file: {}", includes.asString()); + setupConfig(getValidPath({includes.asString()}), ++depth); + } +} + +auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { + if (!a_config_) { + // For the first config + a_config_ = b_config_; + } else if (a_config_.isObject() && b_config_.isObject()) { + for (const auto &key : b_config_.getMemberNames()) { + if (a_config_[key].isObject() && b_config_[key].isObject()) { + mergeConfig(a_config_[key], b_config_[key]); + } else { + a_config_[key] = b_config_[key]; + } + } + } else if (a_config_.isArray() && b_config_.isArray()) { + // This can happen only on the top-level array of a multi-bar config + for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) { + if (a_config_[i].isObject() && b_config_[i].isObject()) { + mergeConfig(a_config_[i], b_config_[i]); + } + } + } else { + spdlog::error("Cannot merge config, conflicting or invalid JSON types"); } } From 8310700bbb52af17477cedc61ea9f660c7965053 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 13 Jul 2021 04:33:12 +0300 Subject: [PATCH 260/355] Improve sway/language --- include/modules/sway/ipc/ipc.hpp | 4 + include/modules/sway/language.hpp | 40 +++++++- include/util/string.hpp | 15 +++ man/waybar-sway-language.5.scd | 59 +++--------- meson.build | 4 +- src/modules/sway/language.cpp | 148 +++++++++++++++++++++++++----- 6 files changed, 197 insertions(+), 73 deletions(-) create mode 100644 include/util/string.hpp diff --git a/include/modules/sway/ipc/ipc.hpp b/include/modules/sway/ipc/ipc.hpp index 2c5a7a6..5f23d17 100644 --- a/include/modules/sway/ipc/ipc.hpp +++ b/include/modules/sway/ipc/ipc.hpp @@ -29,4 +29,8 @@ enum ipc_command_type { IPC_EVENT_BINDING = ((1 << 31) | 5), IPC_EVENT_SHUTDOWN = ((1 << 31) | 6), IPC_EVENT_TICK = ((1 << 31) | 7), + + // sway-specific event types + IPC_EVENT_BAR_STATE_UPDATE = ((1<<31) | 20), + IPC_EVENT_INPUT = ((1<<31) | 21), }; diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index 7cd6bf6..fb4438b 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -1,6 +1,11 @@ #pragma once #include +#include + +#include +#include + #include "ALabel.hpp" #include "bar.hpp" #include "client.hpp" @@ -16,13 +21,40 @@ class Language : public ALabel, public sigc::trackable { auto update() -> void; private: + struct Layout { + std::string full_name; + std::string short_name; + std::string variant; + }; + + class XKBContext { + public: + XKBContext(); + ~XKBContext(); + auto next_layout() -> Layout*; + private: + rxkb_context* context_ = nullptr; + rxkb_layout* xkb_layout_ = nullptr; + Layout* layout_ = nullptr; + }; + void onEvent(const struct Ipc::ipc_response&); void onCmd(const struct Ipc::ipc_response&); + + auto set_current_layout(std::string current_layout) -> void; + auto init_layouts_map(const std::vector& used_layouts) -> void; + + const static std::string XKB_LAYOUT_NAMES_KEY; + const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY; - std::string lang_; - util::JsonParser parser_; - std::mutex mutex_; - Ipc ipc_; + Layout layout_; + std::map layouts_map_; + XKBContext xkb_context_; + bool is_variant_displayed; + + util::JsonParser parser_; + std::mutex mutex_; + Ipc ipc_; }; } // namespace waybar::modules::sway diff --git a/include/util/string.hpp b/include/util/string.hpp new file mode 100644 index 0000000..d644b4c --- /dev/null +++ b/include/util/string.hpp @@ -0,0 +1,15 @@ +#include + +const std::string WHITESPACE = " \n\r\t\f\v"; + +std::string ltrim(const std::string s) { + size_t begin = s.find_first_not_of(WHITESPACE); + return (begin == std::string::npos) ? "" : s.substr(begin); +} + +std::string rtrim(const std::string s) { + size_t end = s.find_last_not_of(WHITESPACE); + return (end == std::string::npos) ? "" : s.substr(0, end + 1); +} + +std::string trim(const std::string& s) { return rtrim(ltrim(s)); } diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index 769924f..f7f8830 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -15,63 +15,30 @@ Addressed by *sway/language* *format*: ++ typeof: string ++ default: {} ++ - The format, how information should be displayed. On {} data gets inserted. - -*rotate*: ++ - typeof: integer ++ - Positive value to rotate the text label. - -*max-length*: ++ - typeof: integer ++ - The maximum length in character the module should display. - -*min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. - -*align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. - -*on-click*: ++ - typeof: string ++ - Command to execute when clicked on the module. - -*on-click-middle*: ++ - typeof: string ++ - Command to execute when middle-clicked on the module using mousewheel. - -*on-click-right*: ++ - typeof: string ++ - Command to execute when you right clicked on the module. - -*on-update*: ++ - typeof: string ++ - Command to execute when the module is updated. - -*on-scroll-up*: ++ - typeof: string ++ - Command to execute when scrolling up on the module. - -*on-scroll-down*: ++ - typeof: string ++ - Command to execute when scrolling down on the module. - -*smooth-scrolling-threshold*: ++ - typeof: double ++ - Threshold to be used when scrolling. + The format, how layout should be displayed. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. +# FORMAT REPLACEMENTS + +*{short}*: Short name of layout (e.g. "en"). Equals to {}. + +*{long}*: Long name of layout (e.g. "English (Dvorak)"). + +*{variant}*: Variant of layout (e.g. "Dvorak"). + # EXAMPLES ``` "sway/language": { "format": "{}", - "max-length": 50 +}, + +"sway/language": { + "format": "{short} {variant}", } ``` diff --git a/meson.build b/meson.build index e80448e..1bd1f02 100644 --- a/meson.build +++ b/meson.build @@ -95,6 +95,7 @@ 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')) +xkbregistry = dependency('xkbregistry') libsndio = compiler.find_library('sndio', required: get_option('sndio')) if libsndio.found() @@ -272,7 +273,8 @@ executable( libmpdclient, gtk_layer_shell, libsndio, - tz_dep + tz_dep, + xkbregistry ], include_directories: [include_directories('include')], install: true, diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 4d27fb8..86a8e1e 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -1,10 +1,24 @@ #include "modules/sway/language.hpp" + +#include #include +#include + +#include +#include +#include + +#include "modules/sway/ipc/ipc.hpp" +#include "util/string.hpp" namespace waybar::modules::sway { +const std::string Language::XKB_LAYOUT_NAMES_KEY = "xkb_layout_names"; +const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name"; + Language::Language(const std::string& id, const Json::Value& config) : ALabel(config, "language", id, "{}", 0, true) { + is_variant_displayed = format_.find("{variant}") != std::string::npos; ipc_.subscribe(R"(["input"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd)); @@ -21,18 +35,31 @@ Language::Language(const std::string& id, const Json::Value& config) } void Language::onCmd(const struct Ipc::ipc_response& res) { + if (res.type != static_cast(IPC_GET_INPUTS)) { + return; + } + try { - auto payload = parser_.parse(res.payload); - //Display current layout of a device with a maximum count of layouts, expecting that all will be OK - Json::Value::ArrayIndex maxId = 0, max = 0; - for(Json::Value::ArrayIndex i = 0; i < payload.size(); i++) { - if(payload[i]["xkb_layout_names"].size() > max) { - max = payload[i]["xkb_layout_names"].size(); - maxId = i; + std::lock_guard lock(mutex_); + auto payload = parser_.parse(res.payload); + std::vector used_layouts; + // Display current layout of a device with a maximum count of layouts, expecting that all will + // be OK + Json::ArrayIndex max_id = 0, max = 0; + for (Json::ArrayIndex i = 0; i < payload.size(); i++) { + auto size = payload[i][XKB_LAYOUT_NAMES_KEY].size(); + if (size > max) { + max = size; + max_id = i; } } - auto layout_name = payload[maxId]["xkb_active_layout_name"].asString().substr(0,2); - lang_ = Glib::Markup::escape_text(layout_name); + + for (const auto& layout : payload[max_id][XKB_LAYOUT_NAMES_KEY]) { + used_layouts.push_back(layout.asString()); + } + + init_layouts_map(used_layouts); + set_current_layout(payload[max_id][XKB_ACTIVE_LAYOUT_NAME_KEY].asString()); dp.emit(); } catch (const std::exception& e) { spdlog::error("Language: {}", e.what()); @@ -40,14 +67,15 @@ void Language::onCmd(const struct Ipc::ipc_response& res) { } void Language::onEvent(const struct Ipc::ipc_response& res) { + if (res.type != static_cast(IPC_EVENT_INPUT)) { + return; + } + try { std::lock_guard lock(mutex_); - auto payload = parser_.parse(res.payload)["input"]; + auto payload = parser_.parse(res.payload)["input"]; if (payload["type"].asString() == "keyboard") { - auto layout_name = payload["xkb_active_layout_name"].asString().substr(0,2); - if (!layout_name.empty()) { - lang_ = Glib::Markup::escape_text(layout_name); - } + set_current_layout(payload[XKB_ACTIVE_LAYOUT_NAME_KEY].asString()); } dp.emit(); } catch (const std::exception& e) { @@ -56,17 +84,93 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { } auto Language::update() -> void { - if (lang_.empty()) { - event_box_.hide(); - } else { - label_.set_markup(fmt::format(format_, lang_)); - if (tooltipEnabled()) { - label_.set_tooltip_text(lang_); - } - event_box_.show(); + auto display_layout = trim(fmt::format(format_, + fmt::arg("short", layout_.short_name), + fmt::arg("long", layout_.full_name), + fmt::arg("variant", layout_.variant))); + label_.set_markup(display_layout); + if (tooltipEnabled()) { + label_.set_tooltip_markup(display_layout); } + + event_box_.show(); + // Call parent update ALabel::update(); } +auto Language::set_current_layout(std::string current_layout) -> void { + layout_ = layouts_map_[current_layout]; +} + +auto Language::init_layouts_map(const std::vector& used_layouts) -> void { + std::map> found_by_short_names; + auto layout = xkb_context_.next_layout(); + for (; layout != nullptr; layout = xkb_context_.next_layout()) { + if (std::find(used_layouts.begin(), used_layouts.end(), layout->full_name) == + used_layouts.end()) { + continue; + } + + if (!is_variant_displayed) { + auto short_name = layout->short_name; + if (found_by_short_names.count(short_name) > 0) { + found_by_short_names[short_name].push_back(layout); + } else { + found_by_short_names[short_name] = {layout}; + } + } + + layouts_map_.emplace(layout->full_name, *layout); + } + + if (is_variant_displayed || found_by_short_names.size() == 0) { + return; + } + + std::map short_name_to_number_map; + for (const auto& used_layout_name : used_layouts) { + auto used_layout = &layouts_map_.find(used_layout_name)->second; + auto layouts_with_same_name_list = found_by_short_names[used_layout->short_name]; + spdlog::info("SIZE: " + std::to_string(layouts_with_same_name_list.size())); + if (layouts_with_same_name_list.size() < 2) { + continue; + } + + if (short_name_to_number_map.count(used_layout->short_name) == 0) { + short_name_to_number_map[used_layout->short_name] = 1; + } + + used_layout->short_name = + used_layout->short_name + std::to_string(short_name_to_number_map[used_layout->short_name]++); + } +} + +Language::XKBContext::XKBContext() { + context_ = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES); + rxkb_context_include_path_append_default(context_); + rxkb_context_parse_default_ruleset(context_); +} + +auto Language::XKBContext::next_layout() -> Layout* { + if (xkb_layout_ == nullptr) { + xkb_layout_ = rxkb_layout_first(context_); + } else { + xkb_layout_ = rxkb_layout_next(xkb_layout_); + } + + if (xkb_layout_ == nullptr) { + return nullptr; + } + + auto description = std::string(rxkb_layout_get_description(xkb_layout_)); + auto name = std::string(rxkb_layout_get_name(xkb_layout_)); + auto variant_ = rxkb_layout_get_variant(xkb_layout_); + std::string variant = variant_ == nullptr ? "" : std::string(variant_); + + layout_ = new Layout{description, name, variant}; + return layout_; +} + +Language::XKBContext::~XKBContext() { rxkb_context_unref(context_); } } // namespace waybar::modules::sway From 9cce5ea6b536a0dace941fd233a0c54c3462b9d8 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 13 Jul 2021 04:49:19 +0300 Subject: [PATCH 261/355] Update dockerfiles --- Dockerfiles/alpine | 2 +- Dockerfiles/archlinux | 2 +- Dockerfiles/debian | 2 +- Dockerfiles/fedora | 2 +- Dockerfiles/opensuse | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfiles/alpine b/Dockerfiles/alpine index 21d1cbb..c0e032f 100644 --- a/Dockerfiles/alpine +++ b/Dockerfiles/alpine @@ -2,4 +2,4 @@ FROM alpine:latest -RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc +RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index e5a53ef..40a1b2e 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,4 +3,4 @@ FROM archlinux:base-devel RUN pacman -Syu --noconfirm && \ - pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm + pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm libxkbcommon diff --git a/Dockerfiles/debian b/Dockerfiles/debian index cee1744..f7f6e63 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon && \ apt-get clean diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index 77c77cb..470ceb7 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -8,5 +8,5 @@ RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ - 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' && \ + 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbcommon)' && \ dnf clean all -y diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index 5b664fb..eb231fd 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -4,4 +4,4 @@ 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 pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc + zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon From 9c2b5efe7b5f9acd934595fcd8f27d2404ea0668 Mon Sep 17 00:00:00 2001 From: Patrick Nicolas Date: Thu, 15 Jul 2021 08:52:02 +0200 Subject: [PATCH 262/355] Support per-device icon in pulseaudio --- include/ALabel.hpp | 2 +- include/modules/pulseaudio.hpp | 2 +- man/waybar-pulseaudio.5.scd | 4 ++++ src/ALabel.cpp | 2 +- src/modules/pulseaudio.cpp | 12 +++++++----- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/include/ALabel.hpp b/include/ALabel.hpp index 5b9ac54..d8a5b50 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -14,7 +14,7 @@ class ALabel : public AModule { virtual ~ALabel() = default; virtual auto update() -> void; virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0); - virtual std::string getIcon(uint16_t, std::vector &alts, uint16_t max = 0); + virtual std::string getIcon(uint16_t, const std::vector &alts, uint16_t max = 0); protected: Gtk::Label label_; diff --git a/include/modules/pulseaudio.hpp b/include/modules/pulseaudio.hpp index 5f17620..9b36dfe 100644 --- a/include/modules/pulseaudio.hpp +++ b/include/modules/pulseaudio.hpp @@ -24,7 +24,7 @@ class Pulseaudio : public ALabel { static void volumeModifyCb(pa_context*, int, void*); bool handleScroll(GdkEventScroll* e); - const std::string getPortIcon() const; + const std::vector getPulseIcon() const; pa_threaded_mainloop* mainloop_; pa_mainloop_api* mainloop_api_; diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index d894290..7de1028 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -109,6 +109,9 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu # ICONS: The following strings for *format-icons* are supported. + +- the device name + If they are found in the current PulseAudio port name, the corresponding icons will be selected. - *default* (Shown, when no other port is found) @@ -131,6 +134,7 @@ If they are found in the current PulseAudio port name, the corresponding icons w "format-bluetooth": "{volume}% {icon}", "format-muted": "", "format-icons": { + "alsa_output.pci-0000_00_1f.3.analog-stereo": "", "headphones": "", "handsfree": "", "headset": "", diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 8d9c9b4..dd41a32 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -76,7 +76,7 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_ return ""; } -std::string ALabel::getIcon(uint16_t percentage, std::vector& alts, uint16_t max) { +std::string ALabel::getIcon(uint16_t percentage, const std::vector& alts, uint16_t max) { auto format_icons = config_["format-icons"]; if (format_icons.isObject()) { std::string _alt = "default"; diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 3fbe956..a7cdcec 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -194,15 +194,17 @@ static const std::array ports = { "phone", }; -const std::string waybar::modules::Pulseaudio::getPortIcon() const { +const std::vector waybar::modules::Pulseaudio::getPulseIcon() const { + std::vector res = {default_sink_name_}; std::string nameLC = port_name_ + form_factor_; std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { if (nameLC.find(port) != std::string::npos) { - return port; + res.push_back(port); + return res; } } - return port_name_; + return res; } auto waybar::modules::Pulseaudio::update() -> void { @@ -252,7 +254,7 @@ auto waybar::modules::Pulseaudio::update() -> void { fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_), fmt::arg("source_desc", source_desc_), - fmt::arg("icon", getIcon(volume_, getPortIcon())))); + fmt::arg("icon", getIcon(volume_, getPulseIcon())))); getState(volume_); if (tooltipEnabled()) { @@ -267,7 +269,7 @@ auto waybar::modules::Pulseaudio::update() -> void { fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_), fmt::arg("source_desc", source_desc_), - fmt::arg("icon", getIcon(volume_, getPortIcon())))); + fmt::arg("icon", getIcon(volume_, getPulseIcon())))); } else { label_.set_tooltip_text(desc_); } From 948eba92a540b52443b1e0b0a414b7ec6de83cab Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 13 Jul 2021 04:49:19 +0300 Subject: [PATCH 263/355] Update dockerfiles --- Dockerfiles/alpine | 2 +- Dockerfiles/archlinux | 2 +- Dockerfiles/debian | 2 +- Dockerfiles/fedora | 2 +- Dockerfiles/opensuse | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfiles/alpine b/Dockerfiles/alpine index 21d1cbb..c0e032f 100644 --- a/Dockerfiles/alpine +++ b/Dockerfiles/alpine @@ -2,4 +2,4 @@ FROM alpine:latest -RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc +RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index e5a53ef..40a1b2e 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,4 +3,4 @@ FROM archlinux:base-devel RUN pacman -Syu --noconfirm && \ - pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm + pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm libxkbcommon diff --git a/Dockerfiles/debian b/Dockerfiles/debian index cee1744..f7f6e63 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon && \ apt-get clean diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index 77c77cb..470ceb7 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -8,5 +8,5 @@ RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ - 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' && \ + 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbcommon)' && \ dnf clean all -y diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index 5b664fb..eb231fd 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -4,4 +4,4 @@ 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 pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc + zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon From 2506c0104a12e017721849ff9b5541b5675e4c7a Mon Sep 17 00:00:00 2001 From: Anakael Date: Fri, 16 Jul 2021 11:37:58 +0300 Subject: [PATCH 264/355] Update Dockerfiles/fedora Co-authored-by: Aleksei Bavshin --- Dockerfiles/fedora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index 470ceb7..a61dcd3 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -8,5 +8,5 @@ RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ - 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbcommon)' && \ + 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' && \ dnf clean all -y From 86a43b904214613b2470db65746367b7721bd929 Mon Sep 17 00:00:00 2001 From: Roosembert Palacios Date: Tue, 20 Jul 2021 10:11:55 +0200 Subject: [PATCH 265/355] pulseaudio: Control currently running sink In a system with multiple sinks, the default sink may not always be the once currently being used. It is more useful to control the currently active sink rather than an unused one. This patch does not make any difference if the system only uses the default sink. Signed-off-by: Roosembert Palacios --- include/modules/pulseaudio.hpp | 3 ++- src/modules/pulseaudio.cpp | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/include/modules/pulseaudio.hpp b/include/modules/pulseaudio.hpp index 5f17620..f636c00 100644 --- a/include/modules/pulseaudio.hpp +++ b/include/modules/pulseaudio.hpp @@ -38,7 +38,8 @@ class Pulseaudio : public ALabel { std::string form_factor_; std::string desc_; std::string monitor_; - std::string default_sink_name_; + std::string current_sink_name_; + bool current_sink_running_; // SOURCE uint32_t source_idx_{0}; uint16_t source_volume_; diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 3fbe956..c0299fc 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -151,8 +151,24 @@ void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const p */ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, int /*eol*/, void *data) { + if (i == nullptr) + return; + auto pa = static_cast(data); - if (i != nullptr && pa->default_sink_name_ == i->name) { + if (pa->current_sink_name_ == i->name) { + if (i->state != PA_SINK_RUNNING) { + pa->current_sink_running_ = false; + } else { + pa->current_sink_running_ = true; + } + } + + if (!pa->current_sink_running_ && i->state == PA_SINK_RUNNING) { + pa->current_sink_name_ = i->name; + pa->current_sink_running_ = true; + } + + if (pa->current_sink_name_ == i->name) { pa->pa_volume_ = i->volume; float volume = static_cast(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM}; pa->sink_idx_ = i->index; @@ -175,11 +191,11 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_ void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i, void *data) { auto pa = static_cast(data); - pa->default_sink_name_ = i->default_sink_name; + pa->current_sink_name_ = i->default_sink_name; pa->default_source_name_ = i->default_source_name; - pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data); - pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data); + pa_context_get_sink_info_list(context, sinkInfoCb, data); + pa_context_get_source_info_list(context, sourceInfoCb, data); } static const std::array ports = { From 6f2bfd43bf2a92ffefd880c6923af757b6565665 Mon Sep 17 00:00:00 2001 From: Lars Christensen Date: Tue, 20 Jul 2021 15:25:05 +0200 Subject: [PATCH 266/355] Fix pulseaudio icon name compilation error --- src/modules/pulseaudio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index bd90375..cf42780 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -211,7 +211,7 @@ static const std::array ports = { }; const std::vector waybar::modules::Pulseaudio::getPulseIcon() const { - std::vector res = {default_sink_name_}; + std::vector res = {default_source_name_}; std::string nameLC = port_name_ + form_factor_; std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { From 6dfa31fb1778da2989a28179304f9e1ccf1e3e4e Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 15:05:11 -0500 Subject: [PATCH 267/355] Basic keyboard state module --- README.md | 2 + include/factory.hpp | 3 ++ include/modules/keyboard_state.hpp | 31 +++++++++++++ meson.build | 7 +++ meson_options.txt | 1 + src/factory.cpp | 5 ++ src/modules/keyboard_state.cpp | 73 ++++++++++++++++++++++++++++++ 7 files changed, 122 insertions(+) create mode 100644 include/modules/keyboard_state.hpp create mode 100644 src/modules/keyboard_state.cpp diff --git a/README.md b/README.md index b104ade..37c0cfc 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ libappindicator-gtk3 [Tray module] libdbusmenu-gtk3 [Tray module] libmpdclient [MPD module] libsndio [sndio module] +libevdev [KeyboardState module] ``` **Build dependencies** @@ -86,6 +87,7 @@ sudo apt install \ clang-tidy \ gobject-introspection \ libdbusmenu-gtk3-dev \ + libevdev-dev \ libfmt-dev \ libgirepository1.0-dev \ libgtk-3-dev \ diff --git a/include/factory.hpp b/include/factory.hpp index 1cae68c..4b9f32a 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -38,6 +38,9 @@ #ifdef HAVE_LIBUDEV #include "modules/backlight.hpp" #endif +#ifdef HAVE_LIBEVDEV +#include "modules/keyboard_state.hpp" +#endif #ifdef HAVE_LIBPULSE #include "modules/pulseaudio.hpp" #endif diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp new file mode 100644 index 0000000..99ed602 --- /dev/null +++ b/include/modules/keyboard_state.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#if FMT_VERSION < 60000 +#include +#else +#include +#endif +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +extern "C" { +#include +} + +namespace waybar::modules { + +class KeyboardState : public ALabel { + public: + KeyboardState(const std::string&, const Json::Value&); + ~KeyboardState(); + auto update() -> void; + + private: + std::string dev_path_; + int fd_; + libevdev* dev_; + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index e80448e..7ac6fd6 100644 --- a/meson.build +++ b/meson.build @@ -94,6 +94,7 @@ 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')) +libevdev = dependency('libevdev', required: get_option('libevdev')) libmpdclient = dependency('libmpdclient', required: get_option('mpd')) libsndio = compiler.find_library('sndio', required: get_option('sndio')) @@ -215,6 +216,11 @@ if libudev.found() and (is_linux or libepoll.found()) src_files += 'src/modules/backlight.cpp' endif +if libevdev.found() and (is_linux or libepoll.found()) + add_project_arguments('-DHAVE_LIBEVDEV', language: 'cpp') + src_files += 'src/modules/keyboard_state.cpp' +endif + if libmpdclient.found() add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') src_files += 'src/modules/mpd/mpd.cpp' @@ -270,6 +276,7 @@ executable( libudev, libepoll, libmpdclient, + libevdev, gtk_layer_shell, libsndio, tz_dep diff --git a/meson_options.txt b/meson_options.txt index cb5581b..fefb3dc 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,6 +1,7 @@ option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.') option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features') option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features') +option('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features') option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio') option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit') option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray') diff --git a/src/factory.cpp b/src/factory.cpp index 1f90789..cab2307 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -70,6 +70,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { return new waybar::modules::Backlight(id, config_[name]); } #endif +#ifdef HAVE_LIBEVDEV + if (ref == "keyboard_state") { + return new waybar::modules::KeyboardState(id, config_[name]); + } +#endif #ifdef HAVE_LIBPULSE if (ref == "pulseaudio") { return new waybar::modules::Pulseaudio(id, config_[name]); diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp new file mode 100644 index 0000000..ea9ae57 --- /dev/null +++ b/src/modules/keyboard_state.cpp @@ -0,0 +1,73 @@ +#include "modules/keyboard_state.hpp" +#include + +extern "C" { +#include +#include +#include +} + +waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Json::Value& config) + : ALabel(config, "keyboard_state", id, "{temperatureC}", 1) { + if (config_["device-path"].isString()) { + dev_path_ = config_["device-path"].asString(); + } else { + dev_path_ = ""; + } + + fd_ = open(dev_path_.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY); + if (fd_ < 0) { + throw std::runtime_error("Can't open " + dev_path_); + } + int err = libevdev_new_from_fd(fd_, &dev_); + if (err < 0) { + throw std::runtime_error("Can't create libevdev device"); + } + if (!libevdev_has_event_type(dev_, EV_LED)) { + throw std::runtime_error("Device doesn't support LED events"); + } + if (!libevdev_has_event_code(dev_, EV_LED, LED_NUML) || !libevdev_has_event_code(dev_, EV_LED, LED_CAPSL)) { + throw std::runtime_error("Device doesn't support num lock or caps lock events"); + } + + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +waybar::modules::KeyboardState::~KeyboardState() { + libevdev_free(dev_); + int err = close(fd_); + if (err < 0) { + // Not much we can do, so ignore it. + } +} + +auto waybar::modules::KeyboardState::update() -> void { + int err = LIBEVDEV_READ_STATUS_SUCCESS; + while (err == LIBEVDEV_READ_STATUS_SUCCESS) { + input_event ev; + err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_NORMAL, &ev); + while (err == LIBEVDEV_READ_STATUS_SYNC) { + err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev); + } + } + if (err != -EAGAIN) { + throw std::runtime_error("Failed to sync evdev device"); + } + + int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML); + //int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL); + + std::string text; + if (numl) { + text = fmt::format(format_, "num lock enabled"); + label_.set_markup(text); + } else { + text = fmt::format(format_, "num lock disabled"); + label_.set_markup(text); + } + + ALabel::update(); +} From 642e28166b784244fda45cc90832b75951969372 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 18:46:39 -0500 Subject: [PATCH 268/355] Add more configuaration --- include/modules/keyboard_state.hpp | 22 ++++++++-- resources/config | 13 +++++- src/factory.cpp | 2 +- src/modules/keyboard_state.cpp | 70 ++++++++++++++++++++++++------ 4 files changed, 89 insertions(+), 18 deletions(-) diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index 99ed602..0873ad5 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -6,8 +6,10 @@ #else #include #endif -#include "ALabel.hpp" +#include "AModule.hpp" +#include "bar.hpp" #include "util/sleeper_thread.hpp" +#include extern "C" { #include @@ -15,16 +17,30 @@ extern "C" { namespace waybar::modules { -class KeyboardState : public ALabel { +class KeyboardState : public AModule { public: - KeyboardState(const std::string&, const Json::Value&); + KeyboardState(const std::string&, const waybar::Bar&, const Json::Value&); ~KeyboardState(); auto update() -> void; private: + const Bar& bar_; + Gtk::Box box_; + Gtk::Label numlock_label_; + Gtk::Label capslock_label_; + Gtk::Label scrolllock_label_; + + std::string numlock_format_; + std::string capslock_format_; + std::string scrolllock_format_; + const std::chrono::seconds interval_; + std::string icon_locked_; + std::string icon_unlocked_; + std::string dev_path_; int fd_; libevdev* dev_; + util::SleeperThread thread_; }; diff --git a/resources/config b/resources/config index 13dc94c..e7c91c0 100644 --- a/resources/config +++ b/resources/config @@ -6,7 +6,7 @@ // 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", "sway/language", "battery", "battery#bat2", "clock", "tray"], + "modules-right": ["keyboard_state", "mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "sway/language", "battery", "battery#bat2", "clock", "tray"], // Modules configuration // "sway/workspaces": { // "disable-scroll": true, @@ -23,6 +23,16 @@ // "default": "" // } // }, + "keyboard_state": { + "numlock": true, + "format": "{name} {icon}", + "format-icons": { + "locked": "", + "unlocked": "" + }, + "capslock": true, + "device-path": "/dev/input/by-path/platform-i8042-serio-0-event-kbd" + }, "sway/mode": { "format": "{}" }, @@ -145,3 +155,4 @@ // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name } } + diff --git a/src/factory.cpp b/src/factory.cpp index cab2307..6a0147d 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -72,7 +72,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { #endif #ifdef HAVE_LIBEVDEV if (ref == "keyboard_state") { - return new waybar::modules::KeyboardState(id, config_[name]); + return new waybar::modules::KeyboardState(id, bar_, config_[name]); } #endif #ifdef HAVE_LIBPULSE diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index ea9ae57..b51617f 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -7,8 +7,44 @@ extern "C" { #include } -waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Json::Value& config) - : ALabel(config, "keyboard_state", id, "{temperatureC}", 1) { +waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config) + : AModule(config, "keyboard_state", id, false, !config["disable-scroll"].asBool()), + bar_(bar), + box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), + numlock_label_(""), + capslock_label_(""), + numlock_format_(config_["format"].isString() ? config_["format"].asString() + : config_["format"]["numlock"].isString() ? config_["format"]["numlock"].asString() + : "{name} {icon}"), + capslock_format_(config_["format"].isString() ? config_["format"].asString() + : config_["format"]["capslock"].isString() ? config_["format"]["capslock"].asString() + : "{name} {icon}"), + scrolllock_format_(config_["format"].isString() ? config_["format"].asString() + : config_["format"]["scrolllock"].isString() ? config_["format"]["scrolllock"].asString() + : "{name} {icon}"), + interval_(std::chrono::seconds(config_["interval"].isUInt() ? config_["interval"].asUInt() : 1)), + icon_locked_(config_["format-icons"]["locked"].isString() + ? config_["format-icons"]["locked"].asString() + : "locked"), + icon_unlocked_(config_["format-icons"]["unlocked"].isString() + ? config_["format-icons"]["unlocked"].asString() + : "unlocked") + { + box_.set_name("keyboard_state"); + if (config_["numlock"].asBool()) { + box_.pack_end(numlock_label_, false, false, 0); + } + if (config_["capslock"].asBool()) { + box_.pack_end(capslock_label_, false, false, 0); + } + if (config_["scrolllock"].asBool()) { + box_.pack_end(scrolllock_label_, false, false, 0); + } + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + event_box_.add(box_); + if (config_["device-path"].isString()) { dev_path_ = config_["device-path"].asString(); } else { @@ -26,8 +62,10 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Json: if (!libevdev_has_event_type(dev_, EV_LED)) { throw std::runtime_error("Device doesn't support LED events"); } - if (!libevdev_has_event_code(dev_, EV_LED, LED_NUML) || !libevdev_has_event_code(dev_, EV_LED, LED_CAPSL)) { - throw std::runtime_error("Device doesn't support num lock or caps lock events"); + if (!libevdev_has_event_code(dev_, EV_LED, LED_NUML) + || !libevdev_has_event_code(dev_, EV_LED, LED_CAPSL) + || !libevdev_has_event_code(dev_, EV_LED, LED_SCROLLL)) { + throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events"); } thread_ = [this] { @@ -58,16 +96,22 @@ auto waybar::modules::KeyboardState::update() -> void { } int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML); - //int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL); + int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL); + int scrolll = libevdev_get_event_value(dev_, EV_LED, LED_SCROLLL); std::string text; - if (numl) { - text = fmt::format(format_, "num lock enabled"); - label_.set_markup(text); - } else { - text = fmt::format(format_, "num lock disabled"); - label_.set_markup(text); - } + text = fmt::format(numlock_format_, + fmt::arg("icon", numl ? icon_locked_ : icon_unlocked_), + fmt::arg("name", "Num")); + numlock_label_.set_markup(text); + text = fmt::format(capslock_format_, + fmt::arg("icon", capsl ? icon_locked_ : icon_unlocked_), + fmt::arg("name", "Caps")); + capslock_label_.set_markup(text); + text = fmt::format(scrolllock_format_, + fmt::arg("icon", scrolll ? icon_locked_ : icon_unlocked_), + fmt::arg("name", "Scroll")); + scrolllock_label_.set_markup(text); - ALabel::update(); + AModule::update(); } From 40e6360722acc32ea1b266f361666a4cc64d9608 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 18:57:12 -0500 Subject: [PATCH 269/355] Update css class when locked/unlocked --- src/modules/keyboard_state.cpp | 35 +++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index b51617f..516502f 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -99,19 +99,28 @@ auto waybar::modules::KeyboardState::update() -> void { int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL); int scrolll = libevdev_get_event_value(dev_, EV_LED, LED_SCROLLL); - std::string text; - text = fmt::format(numlock_format_, - fmt::arg("icon", numl ? icon_locked_ : icon_unlocked_), - fmt::arg("name", "Num")); - numlock_label_.set_markup(text); - text = fmt::format(capslock_format_, - fmt::arg("icon", capsl ? icon_locked_ : icon_unlocked_), - fmt::arg("name", "Caps")); - capslock_label_.set_markup(text); - text = fmt::format(scrolllock_format_, - fmt::arg("icon", scrolll ? icon_locked_ : icon_unlocked_), - fmt::arg("name", "Scroll")); - scrolllock_label_.set_markup(text); + struct { + bool state; + Gtk::Label& label; + const std::string& format; + const char* name; + } label_states[] = { + {(bool) numl, numlock_label_, numlock_format_, "Num"}, + {(bool) capsl, capslock_label_, capslock_format_, "Caps"}, + {(bool) scrolll, scrolllock_label_, scrolllock_format_, "Scroll"}, + }; + for (auto& label_state : label_states) { + std::string text; + text = fmt::format(label_state.format, + fmt::arg("icon", label_state.state ? icon_locked_ : icon_unlocked_), + fmt::arg("name", label_state.name)); + label_state.label.set_markup(text); + if (label_state.state) { + label_state.label.get_style_context()->add_class("locked"); + } else { + label_state.label.get_style_context()->remove_class("locked"); + } + } AModule::update(); } From 6fdbc27998e16daafa637427c6ecddc53abc31ce Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 19:06:55 -0500 Subject: [PATCH 270/355] Add default style --- resources/config | 2 +- resources/style.css | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/resources/config b/resources/config index e7c91c0..a83edf1 100644 --- a/resources/config +++ b/resources/config @@ -6,7 +6,7 @@ // Choose the order of the modules "modules-left": ["sway/workspaces", "sway/mode", "custom/media"], "modules-center": ["sway/window"], - "modules-right": ["keyboard_state", "mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "sway/language", "battery", "battery#bat2", "clock", "tray"], + "modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "keyboard_state", "sway/language", "battery", "battery#bat2", "clock", "tray"], // Modules configuration // "sway/workspaces": { // "disable-scroll": true, diff --git a/resources/style.css b/resources/style.css index 63ea871..c1ac5ad 100644 --- a/resources/style.css +++ b/resources/style.css @@ -228,3 +228,19 @@ label:focus { margin: 0 5px; min-width: 16px; } + +#keyboard_state { + background: #97e1ad; + color: #000000; + padding: 0 0px; + margin: 0 5px; + min-width: 16px; +} + +#keyboard_state > label { + padding: 0 5px; +} + +#keyboard_state > label.locked { + background: rgba(0, 0, 0, 0.2); +} From 08e886ebc681849e7b241db71ad7e76a9d0dec43 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 21:05:34 -0500 Subject: [PATCH 271/355] Search for device automatically if none given --- include/modules/keyboard_state.hpp | 3 +- resources/config | 5 +-- src/modules/keyboard_state.cpp | 69 +++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index 0873ad5..d6d98de 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -24,6 +24,8 @@ class KeyboardState : public AModule { auto update() -> void; private: + static auto openDevice(const std::string&) -> std::pair; + const Bar& bar_; Gtk::Box box_; Gtk::Label numlock_label_; @@ -37,7 +39,6 @@ class KeyboardState : public AModule { std::string icon_locked_; std::string icon_unlocked_; - std::string dev_path_; int fd_; libevdev* dev_; diff --git a/resources/config b/resources/config index a83edf1..465f694 100644 --- a/resources/config +++ b/resources/config @@ -25,13 +25,12 @@ // }, "keyboard_state": { "numlock": true, + "capslock": true, "format": "{name} {icon}", "format-icons": { "locked": "", "unlocked": "" - }, - "capslock": true, - "device-path": "/dev/input/by-path/platform-i8042-serio-0-event-kbd" + } }, "sway/mode": { "format": "{}" diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 516502f..9a7802f 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -1,5 +1,6 @@ #include "modules/keyboard_state.hpp" #include +#include extern "C" { #include @@ -28,8 +29,9 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& : "locked"), icon_unlocked_(config_["format-icons"]["unlocked"].isString() ? config_["format-icons"]["unlocked"].asString() - : "unlocked") - { + : "unlocked"), + fd_(0), + dev_(nullptr) { box_.set_name("keyboard_state"); if (config_["numlock"].asBool()) { box_.pack_end(numlock_label_, false, false, 0); @@ -46,26 +48,28 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& event_box_.add(box_); if (config_["device-path"].isString()) { - dev_path_ = config_["device-path"].asString(); + std::string dev_path = config_["device-path"].asString(); + std::tie(fd_, dev_) = openDevice(dev_path); } else { - dev_path_ = ""; - } - - fd_ = open(dev_path_.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY); - if (fd_ < 0) { - throw std::runtime_error("Can't open " + dev_path_); - } - int err = libevdev_new_from_fd(fd_, &dev_); - if (err < 0) { - throw std::runtime_error("Can't create libevdev device"); - } - if (!libevdev_has_event_type(dev_, EV_LED)) { - throw std::runtime_error("Device doesn't support LED events"); - } - if (!libevdev_has_event_code(dev_, EV_LED, LED_NUML) - || !libevdev_has_event_code(dev_, EV_LED, LED_CAPSL) - || !libevdev_has_event_code(dev_, EV_LED, LED_SCROLLL)) { - throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events"); + DIR* dev_dir = opendir("/dev/input"); + if (dev_dir == nullptr) { + throw std::runtime_error("Failed to open /dev/input"); + } + dirent *ep; + while ((ep = readdir(dev_dir))) { + if (ep->d_type != DT_CHR) continue; + std::string dev_path = std::string("/dev/input/") + ep->d_name; + try { + std::tie(fd_, dev_) = openDevice(dev_path); + spdlog::info("Found device {} at '{}'", libevdev_get_name(dev_), dev_path); + break; + } catch (const std::runtime_error& e) { + continue; + } + } + if (dev_ == nullptr) { + throw std::runtime_error("Failed to find keyboard device"); + } } thread_ = [this] { @@ -82,6 +86,29 @@ waybar::modules::KeyboardState::~KeyboardState() { } } +auto waybar::modules::KeyboardState::openDevice(const std::string& path) -> std::pair { + int fd = open(path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY); + if (fd < 0) { + throw std::runtime_error("Can't open " + path); + } + + libevdev* dev; + int err = libevdev_new_from_fd(fd, &dev); + if (err < 0) { + throw std::runtime_error("Can't create libevdev device"); + } + if (!libevdev_has_event_type(dev, EV_LED)) { + throw std::runtime_error("Device doesn't support LED events"); + } + if (!libevdev_has_event_code(dev, EV_LED, LED_NUML) + || !libevdev_has_event_code(dev, EV_LED, LED_CAPSL) + || !libevdev_has_event_code(dev, EV_LED, LED_SCROLLL)) { + throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events"); + } + + return std::make_pair(fd, dev); +} + auto waybar::modules::KeyboardState::update() -> void { int err = LIBEVDEV_READ_STATUS_SUCCESS; while (err == LIBEVDEV_READ_STATUS_SUCCESS) { From 99138ffdcd6e3620a9952d7ca1ceb7b0fc5edfb1 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Thu, 15 Apr 2021 17:41:15 -0400 Subject: [PATCH 272/355] Add man page for keyboard_state module --- man/waybar-keyboard-state.5.scd | 80 +++++++++++++++++++++++++++++++++ man/waybar.5.scd.in | 1 + meson.build | 1 + 3 files changed, 82 insertions(+) create mode 100644 man/waybar-keyboard-state.5.scd diff --git a/man/waybar-keyboard-state.5.scd b/man/waybar-keyboard-state.5.scd new file mode 100644 index 0000000..8e9ca5e --- /dev/null +++ b/man/waybar-keyboard-state.5.scd @@ -0,0 +1,80 @@ +waybar-keyboard-state(5) + +# NAME + +waybar - keyboard_state module + +# DESCRIPTION + +The *keyboard_state* module displays the state of number lock, caps lock, and scroll lock. + +# CONFIGURATION + +*interval*: ++ + typeof: integer ++ + default: 1 ++ + The interval, in seconds, to poll the keyboard state. + +*format*: ++ + typeof: string|object ++ + default: {name} {icon} ++ + The format, how information should be displayed. If a string, the same format is used for all keyboard states. If an object, the fields "numlock", "capslock", and "scrolllock" each specify the format for the corresponding state. Any unspecified states use the default format. + +*format-icons*: ++ + typeof: object ++ + default: {"locked": "locked", "unlocked": "unlocked"} ++ + Based on the keyboard state, the corresponding icon gets selected. The same set of icons is used for number, caps, and scroll lock, but the icon is selected from the set independently for each. See *icons*. + +*numlock*: ++ + typeof: bool ++ + default: false ++ + Display the number lock state. + +*capslock*: ++ + typeof: bool ++ + default: false ++ + Display the caps lock state. + +*scrolllock*: ++ + typeof: bool ++ + default: false ++ + Display the scroll lock state. + +*device-path*: ++ + typeof: string ++ + default: chooses first valid input device ++ + Which libevdev input device to show the state of. Libevdev devices can be found in /dev/input. The device should support number lock, caps lock, and scroll lock events. + +# FORMAT REPLACEMENTS + +*{name}*: Caps, Num, or Scroll. + +*{icon}*: Icon, as defined in *format-icons*. + +# ICONS + +The following *format-icons* can be set. + +- *locked*: Will be shown when the keyboard state is locked. Default "locked". +- *unlocked*: Will be shown when the keyboard state is not locked. Default "unlocked" + +# EXAMPLE: + +``` +"keyboard_state": { + "numlock": true, + "capslock": true, + "format": "{name} {icon}", + "format-icons": { + "locked": "", + "unlocked": "" + } +} +``` + +# STYLE + +- *#keyboard_state* +- *#keyboard_state label* +- *#keyboard_state label.locked* + diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 0168de3..9dc6925 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -208,6 +208,7 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270. - *waybar-custom(5)* - *waybar-disk(5)* - *waybar-idle-inhibitor(5)* +- *waybar-keyboard-state(5)* - *waybar-memory(5)* - *waybar-mpd(5)* - *waybar-network(5)* diff --git a/meson.build b/meson.build index 7ac6fd6..f3b50f7 100644 --- a/meson.build +++ b/meson.build @@ -317,6 +317,7 @@ if scdoc.found() 'waybar-custom.5.scd', 'waybar-disk.5.scd', 'waybar-idle-inhibitor.5.scd', + 'waybar-keyboard-state.5.scd', 'waybar-memory.5.scd', 'waybar-mpd.5.scd', 'waybar-network.5.scd', From 9880c6929f64284aa088a8a662ac3c636176b685 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Tue, 20 Jul 2021 21:24:43 -0400 Subject: [PATCH 273/355] Install libevdev in FreeBSD workflow --- .github/workflows/freebsd.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 2fde610..f02c9b5 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -15,8 +15,9 @@ jobs: export CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib # sndio sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf pkg install -y git # subprojects/date - pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu sndio \ - libfmt libmpdclient libudev-devd meson pkgconf pulseaudio scdoc spdlog + pkg install -y evdev-proto gtk-layer-shell gtkmm30 jsoncpp libdbusmenu \ + libevdev libfmt libmpdclient libudev-devd meson pkgconf pulseaudio \ + scdoc sndio spdlog run: | meson build -Dman-pages=enabled ninja -C build From 311c5779ea7729474343490817974a6c89d6fe0c Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Tue, 20 Jul 2021 23:01:39 -0400 Subject: [PATCH 274/355] Remove unused variable --- include/modules/keyboard_state.hpp | 1 - src/modules/keyboard_state.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index d6d98de..1793bfe 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -26,7 +26,6 @@ class KeyboardState : public AModule { private: static auto openDevice(const std::string&) -> std::pair; - const Bar& bar_; Gtk::Box box_; Gtk::Label numlock_label_; Gtk::Label capslock_label_; diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 9a7802f..ca72144 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -10,7 +10,6 @@ extern "C" { waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config) : AModule(config, "keyboard_state", id, false, !config["disable-scroll"].asBool()), - bar_(bar), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), numlock_label_(""), capslock_label_(""), From 1440ed29d45f978164f37dd86c3e0e0bcaadef85 Mon Sep 17 00:00:00 2001 From: Michael Swiger Date: Tue, 20 Jul 2021 22:29:34 -0700 Subject: [PATCH 275/355] Fix blurry tray icons for HiDPI displays --- src/modules/sni/item.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index abf9556..6fca472 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -1,4 +1,5 @@ #include "modules/sni/item.hpp" +#include #include #include #include @@ -252,33 +253,42 @@ Glib::RefPtr Item::extractPixBuf(GVariant* variant) { } void Item::updateImage() { + auto scale_factor = image.get_scale_factor(); + auto scaled_icon_size = icon_size * scale_factor; + image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU); - image.set_pixel_size(icon_size); + image.set_pixel_size(scaled_icon_size); if (!icon_name.empty()) { try { // Try to find icons specified by path and filename std::ifstream temp(icon_name); if (temp.is_open()) { 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 - // Keep the aspect ratio and scale to make the height equal to icon_size + // Keep the aspect ratio and scale to make the height equal to scaled_icon_size // If people have non square icons, assume they want it to grow in width not height - int width = icon_size * pixbuf->get_width() / pixbuf->get_height(); + int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height(); - pixbuf = pixbuf->scale_simple(width, icon_size, Gdk::InterpType::INTERP_BILINEAR); - image.set(pixbuf); + pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); + + auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, 0, image.get_window()); + image.set(surface); } } else { - image.set(getIconByName(icon_name, icon_size)); + auto icon_by_name = getIconByName(icon_name, scaled_icon_size); + auto surface = Gdk::Cairo::create_surface_from_pixbuf(icon_by_name, 0, image.get_window()); + image.set(surface); } } 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); + icon_pixmap = icon_pixmap->scale_simple(icon_size, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); + auto surface = Gdk::Cairo::create_surface_from_pixbuf(icon_pixmap, 0, image.get_window()); image.set(icon_pixmap); } } From 67d482d28b46ee6f89d69016ec3451bbe8653f11 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Jul 2021 09:23:52 +0200 Subject: [PATCH 276/355] Update opensuse --- Dockerfiles/opensuse | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index eb231fd..1015e5c 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -3,5 +3,7 @@ FROM opensuse/tumbleweed:latest RUN zypper -n up && \ + zypper -n addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo && \ + zypper -n refresh && \ zypper -n install -t pattern devel_C_C++ && \ zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon From 1f5c07a07f5f35dd531177ad1e86251b0a6565a4 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Jul 2021 09:27:54 +0200 Subject: [PATCH 277/355] Update debian --- Dockerfiles/debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index f7f6e63..7c9e0b0 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev && \ apt-get clean From 7f5fd1ac8648d1301c2840d328567c120317b285 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Jul 2021 09:30:47 +0200 Subject: [PATCH 278/355] Update opensuse --- Dockerfiles/opensuse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index 1015e5c..a88aa7d 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -3,7 +3,7 @@ FROM opensuse/tumbleweed:latest RUN zypper -n up && \ - zypper -n addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo && \ + zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \ zypper -n refresh && \ zypper -n install -t pattern devel_C_C++ && \ zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon From 7729ca34275bbc7a2bcd382a14f5be6cc74396e9 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Jul 2021 10:28:56 +0200 Subject: [PATCH 279/355] Update debian --- Dockerfiles/debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 7c9e0b0..32d7a6f 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry0 && \ apt-get clean From 929fc16994c413923af31d8b4a3d302315943a47 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 23 Jun 2021 00:27:30 -0700 Subject: [PATCH 280/355] fix(tray): ignore unused WindowId property --- include/modules/sni/item.hpp | 1 - src/modules/sni/item.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index 3cbd0b7..11402c1 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -30,7 +30,6 @@ class Item : public sigc::trackable { std::string status; std::string title; - int32_t window_id; std::string icon_name; Glib::RefPtr icon_pixmap; Glib::RefPtr icon_theme; diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 6fca472..0ccba19 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -105,8 +105,6 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { 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") { From 4b6253e81001e285c7e09a7196b3dad98d9aa783 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 23 Jun 2021 00:27:42 -0700 Subject: [PATCH 281/355] refactor(tray): infer changed properties from signal name Comparing two GVariants is too expensive; let's collect the set of properties updated by each signal and apply them unconditionally. --- include/modules/sni/item.hpp | 5 +++- src/modules/sni/item.cpp | 46 ++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index 11402c1..fbb5163 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -11,6 +11,9 @@ #include #include +#include +#include + namespace waybar::modules::SNI { class Item : public sigc::trackable { @@ -64,7 +67,7 @@ class Item : public sigc::trackable { Glib::RefPtr proxy_; Glib::RefPtr cancellable_; - bool update_pending_; + std::set update_pending_; }; } // namespace waybar::modules::SNI diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 0ccba19..d3eed4a 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -1,8 +1,11 @@ #include "modules/sni/item.hpp" + #include #include #include + #include +#include template <> struct fmt::formatter : formatter { @@ -40,8 +43,7 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf object_path(op), icon_size(16), effective_icon_size(0), - icon_theme(Gtk::IconTheme::create()), - update_pending_(false) { + icon_theme(Gtk::IconTheme::create()) { if (config["icon-size"].isUInt()) { icon_size = config["icon-size"].asUInt(); } @@ -148,8 +150,6 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } 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", @@ -166,10 +166,7 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { 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); + if (update_pending_.count(name.raw())) { setProperty(name, const_cast(value)); } } @@ -181,18 +178,37 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { } catch (const std::exception& err) { spdlog::warn("Failed to update properties: {}", err.what()); } + update_pending_.clear(); } +/** + * Mapping from a signal name to a set of possibly changed properties. + * Commented signals are not handled by the tray module at the moment. + */ +static const std::map> signal2props = { + {"NewTitle", {"Title"}}, + {"NewIcon", {"IconName", "IconPixmap"}}, + // {"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}}, + // {"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}}, + {"NewIconThemePath", {"IconThemePath"}}, + {"NewToolTip", {"ToolTip"}}, + {"NewStatus", {"Status"}}, + // {"XAyatanaNewLabel", {"XAyatanaLabel"}}, +}; + void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, 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); + auto changed = signal2props.find(signal_name.raw()); + if (changed != signal2props.end()) { + if (update_pending_.empty()) { + /* Debounce signals and schedule update of all properties. + * Based on behavior of Plasma dataengine for StatusNotifierItem. + */ + Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties), + UPDATE_DEBOUNCE_TIME); + } + update_pending_.insert(changed->second.begin(), changed->second.end()); } } From 84a8f79bbe18ec17036f7816d29cc6c5df20b0de Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 23 Jun 2021 23:47:35 -0700 Subject: [PATCH 282/355] feat(tray): implement tooltips (text only) for tray items --- include/modules/sni/item.hpp | 6 ++++++ src/modules/sni/item.cpp | 24 ++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index fbb5163..fc4596e 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -16,6 +16,11 @@ namespace waybar::modules::SNI { +struct ToolTip { + Glib::ustring icon_name; + Glib::ustring text; +}; + class Item : public sigc::trackable { public: Item(const std::string&, const std::string&, const Json::Value&); @@ -41,6 +46,7 @@ class Item : public sigc::trackable { std::string attention_movie_name; std::string icon_theme_path; std::string menu; + ToolTip tooltip; DbusmenuGtkMenu* dbus_menu = nullptr; Gtk::Menu* gtk_menu = nullptr; /** diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index d3eed4a..f092183 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -81,7 +82,6 @@ void Item::proxyReady(Glib::RefPtr& result) { 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()); @@ -91,10 +91,24 @@ void Item::proxyReady(Glib::RefPtr& result) { } template -T get_variant(Glib::VariantBase& value) { +T get_variant(const Glib::VariantBase& value) { return Glib::VariantBase::cast_dynamic>(value).get(); } +template <> +ToolTip get_variant(const Glib::VariantBase& value) { + ToolTip result; + // Unwrap (sa(iiay)ss) + auto container = value.cast_dynamic(value); + result.icon_name = get_variant(container.get_child(0)); + result.text = get_variant(container.get_child(2)); + auto description = get_variant(container.get_child(3)); + if (!description.empty()) { + result.text = fmt::format("{}\n{}", result.text, description); + } + return result; +} + void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { try { spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value); @@ -122,7 +136,10 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } else if (name == "AttentionMovieName") { attention_movie_name = get_variant(value); } else if (name == "ToolTip") { - // TODO: tooltip + tooltip = get_variant(value); + if (!tooltip.text.empty()) { + event_box.set_tooltip_markup(tooltip.text); + } } else if (name == "IconThemePath") { icon_theme_path = get_variant(value); if (!icon_theme_path.empty()) { @@ -172,7 +189,6 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { } 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) { From 1418f96e4653aad2e8f0be25453ac022632cbe32 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 30 Jun 2021 22:17:27 -0700 Subject: [PATCH 283/355] feat(tray): fallback to Title for items without ToolTip --- src/modules/sni/item.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index f092183..d4c4872 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -119,6 +119,9 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { id = get_variant(value); } else if (name == "Title") { title = get_variant(value); + if (tooltip.text.empty()) { + event_box.set_tooltip_markup(title); + } } else if (name == "Status") { status = get_variant(value); } else if (name == "IconName") { From 245f7f4b11731c158f15703750b85dd6c87d660f Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 26 Jun 2021 16:33:25 -0700 Subject: [PATCH 284/355] feat(tray): handle scroll events --- include/modules/sni/item.hpp | 6 ++++ man/waybar-tray.5.scd | 4 +++ src/modules/sni/item.cpp | 54 +++++++++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index fc4596e..e51d3c7 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -70,6 +70,12 @@ class Item : public sigc::trackable { static void onMenuDestroyed(Item* self, GObject* old_menu_pointer); void makeMenu(); bool handleClick(GdkEventButton* const& /*ev*/); + bool handleScroll(GdkEventScroll* const&); + + // smooth scrolling threshold + gdouble scroll_threshold_ = 0; + gdouble distance_scrolled_x_ = 0; + gdouble distance_scrolled_y_ = 0; Glib::RefPtr proxy_; Glib::RefPtr cancellable_; diff --git a/man/waybar-tray.5.scd b/man/waybar-tray.5.scd index cd0e93f..5446a56 100644 --- a/man/waybar-tray.5.scd +++ b/man/waybar-tray.5.scd @@ -16,6 +16,10 @@ Addressed by *tray* typeof: integer ++ Defines the size of the tray icons. +*smooth-scrolling-threshold*: ++ + typeof: double ++ + Threshold to be used when scrolling. + *spacing*: ++ typeof: integer ++ Defines the spacing between the tray icons. diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index d4c4872..9a77d24 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -48,9 +48,13 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf if (config["icon-size"].isUInt()) { icon_size = config["icon-size"].asUInt(); } + if (config["smooth-scrolling-threshold"].isNumeric()) { + scroll_threshold_ = config["smooth-scrolling-threshold"].asDouble(); + } event_box.add(image); - event_box.add_events(Gdk::BUTTON_PRESS_MASK); + event_box.add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick)); + event_box.signal_scroll_event().connect(sigc::mem_fun(*this, &Item::handleScroll)); cancellable_ = Gio::Cancellable::create(); @@ -403,4 +407,52 @@ bool Item::handleClick(GdkEventButton* const& ev) { return false; } +bool Item::handleScroll(GdkEventScroll* const& ev) { + int dx = 0, dy = 0; + switch (ev->direction) { + case GDK_SCROLL_UP: + dy = -1; + break; + case GDK_SCROLL_DOWN: + dy = 1; + break; + case GDK_SCROLL_LEFT: + dx = -1; + break; + case GDK_SCROLL_RIGHT: + dx = 1; + break; + case GDK_SCROLL_SMOOTH: + distance_scrolled_x_ += ev->delta_x; + distance_scrolled_y_ += ev->delta_y; + // check against the configured threshold and ensure that the absolute value >= 1 + if (distance_scrolled_x_ > scroll_threshold_) { + dx = (int)lround(std::max(distance_scrolled_x_, 1.0)); + distance_scrolled_x_ = 0; + } else if (distance_scrolled_x_ < -scroll_threshold_) { + dx = (int)lround(std::min(distance_scrolled_x_, -1.0)); + distance_scrolled_x_ = 0; + } + if (distance_scrolled_y_ > scroll_threshold_) { + dy = (int)lround(std::max(distance_scrolled_y_, 1.0)); + distance_scrolled_y_ = 0; + } else if (distance_scrolled_y_ < -scroll_threshold_) { + dy = (int)lround(std::min(distance_scrolled_y_, -1.0)); + distance_scrolled_y_ = 0; + } + break; + } + if (dx != 0) { + auto parameters = Glib::VariantContainerBase::create_tuple( + {Glib::Variant::create(dx), Glib::Variant::create("horizontal")}); + proxy_->call("Scroll", parameters); + } + if (dy != 0) { + auto parameters = Glib::VariantContainerBase::create_tuple( + {Glib::Variant::create(dy), Glib::Variant::create("vertical")}); + proxy_->call("Scroll", parameters); + } + return true; +} + } // namespace waybar::modules::SNI From a5fe6f40b8d1439b8c9794e30ba97d92fabc4715 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 2 Jul 2021 19:44:10 -0700 Subject: [PATCH 285/355] feat(tray): handle Status property On the `Passive` value of `Status` tray items would be hidden unless `show-passive-items` is set to true. On the `NeedsAttention` value of `Status` tray items will have a `.needs-attention` CSS class. --- include/modules/sni/item.hpp | 4 +++- man/waybar-tray.5.scd | 8 ++++++++ resources/style.css | 9 +++++++++ src/modules/sni/item.cpp | 24 ++++++++++++++++++++++-- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index e51d3c7..1f1503e 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -35,7 +35,6 @@ class Item : public sigc::trackable { Gtk::EventBox event_box; std::string category; std::string id; - std::string status; std::string title; std::string icon_name; @@ -59,6 +58,7 @@ class Item : public sigc::trackable { private: void proxyReady(Glib::RefPtr& result); void setProperty(const Glib::ustring& name, Glib::VariantBase& value); + void setStatus(const Glib::ustring& value); void getUpdatedProperties(); void processUpdatedProperties(Glib::RefPtr& result); void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, @@ -76,6 +76,8 @@ class Item : public sigc::trackable { gdouble scroll_threshold_ = 0; gdouble distance_scrolled_x_ = 0; gdouble distance_scrolled_y_ = 0; + // visibility of items with Status == Passive + bool show_passive_ = false; Glib::RefPtr proxy_; Glib::RefPtr cancellable_; diff --git a/man/waybar-tray.5.scd b/man/waybar-tray.5.scd index 5446a56..c664594 100644 --- a/man/waybar-tray.5.scd +++ b/man/waybar-tray.5.scd @@ -16,6 +16,11 @@ Addressed by *tray* typeof: integer ++ Defines the size of the tray icons. +*show-passive-items*: ++ + typeof: bool ++ + default: false ++ + Defines visibility of the tray icons with *Passive* status. + *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. @@ -41,3 +46,6 @@ Addressed by *tray* # STYLE - *#tray* +- *#tray > .passive* +- *#tray > .active* +- *#tray > .needs-attention* diff --git a/resources/style.css b/resources/style.css index 63ea871..32dce42 100644 --- a/resources/style.css +++ b/resources/style.css @@ -195,6 +195,15 @@ label:focus { background-color: #2980b9; } +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #eb4d4b; +} + #idle_inhibitor { background-color: #2d3436; } diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 9a77d24..267cf63 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -51,10 +51,15 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf if (config["smooth-scrolling-threshold"].isNumeric()) { scroll_threshold_ = config["smooth-scrolling-threshold"].asDouble(); } + if (config["show-passive-items"].isBool()) { + show_passive_ = config["show-passive-items"].asBool(); + } event_box.add(image); event_box.add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick)); event_box.signal_scroll_event().connect(sigc::mem_fun(*this, &Item::handleScroll)); + // initial visibility + event_box.set_visible(show_passive_); cancellable_ = Gio::Cancellable::create(); @@ -81,7 +86,7 @@ void Item::proxyReady(Glib::RefPtr& result) { this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal)); - if (this->id.empty() || this->category.empty() || this->status.empty()) { + if (this->id.empty() || this->category.empty()) { spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path); return; } @@ -127,7 +132,7 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { event_box.set_tooltip_markup(title); } } else if (name == "Status") { - status = get_variant(value); + setStatus(get_variant(value)); } else if (name == "IconName") { icon_name = get_variant(value); } else if (name == "IconPixmap") { @@ -173,6 +178,21 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } } +void Item::setStatus(const Glib::ustring& value) { + Glib::ustring lower = value.lowercase(); + event_box.set_visible(show_passive_ || lower.compare("passive") != 0); + + auto style = event_box.get_style_context(); + for (const auto& class_name : style->list_classes()) { + style->remove_class(class_name); + } + if (lower.compare("needsattention") == 0) { + // convert status to dash-case for CSS + lower = "needs-attention"; + } + style->add_class(lower); +} + void Item::getUpdatedProperties() { auto params = Glib::VariantContainerBase::create_tuple( {Glib::Variant::create(SNI_INTERFACE_NAME)}); From cf832798fb4ac250fb1ff0435e8f8f3c5d384b30 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Jul 2021 14:46:03 +0200 Subject: [PATCH 286/355] Update debian --- Dockerfiles/debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 32d7a6f..026d8fd 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry0 && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 && \ apt-get clean From 77a2eff2ce828eb14105c076f84333531b953ec9 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Jul 2021 14:49:03 +0200 Subject: [PATCH 287/355] Update opensuse --- Dockerfiles/opensuse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index a88aa7d..d7cdcf6 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -6,4 +6,4 @@ RUN zypper -n up && \ zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \ zypper -n refresh && \ zypper -n install -t pattern devel_C_C++ && \ - zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon + zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc From 2009ceb35093fcf24c23d1d20e024fc3bfc58c35 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Jul 2021 15:01:29 +0200 Subject: [PATCH 288/355] Update opensuse --- Dockerfiles/opensuse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index d7cdcf6..49dea27 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -6,4 +6,4 @@ RUN zypper -n up && \ zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \ zypper -n refresh && \ zypper -n install -t pattern devel_C_C++ && \ - zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc + zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc From 88a5f713ed2c98034dbdd547d184e56a3071c5c8 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Fri, 23 Jul 2021 09:45:07 -0400 Subject: [PATCH 289/355] Prefer keyboard-state over keyboard_state --- man/waybar-keyboard-state.5.scd | 12 ++++++------ resources/config | 4 ++-- resources/style.css | 6 +++--- src/factory.cpp | 2 +- src/modules/keyboard_state.cpp | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/man/waybar-keyboard-state.5.scd b/man/waybar-keyboard-state.5.scd index 8e9ca5e..1d7c3a8 100644 --- a/man/waybar-keyboard-state.5.scd +++ b/man/waybar-keyboard-state.5.scd @@ -2,11 +2,11 @@ waybar-keyboard-state(5) # NAME -waybar - keyboard_state module +waybar - keyboard-state module # DESCRIPTION -The *keyboard_state* module displays the state of number lock, caps lock, and scroll lock. +The *keyboard-state* module displays the state of number lock, caps lock, and scroll lock. # CONFIGURATION @@ -61,7 +61,7 @@ The following *format-icons* can be set. # EXAMPLE: ``` -"keyboard_state": { +"keyboard-state": { "numlock": true, "capslock": true, "format": "{name} {icon}", @@ -74,7 +74,7 @@ The following *format-icons* can be set. # STYLE -- *#keyboard_state* -- *#keyboard_state label* -- *#keyboard_state label.locked* +- *#keyboard-state* +- *#keyboard-state label* +- *#keyboard-state label.locked* diff --git a/resources/config b/resources/config index 465f694..87f24c0 100644 --- a/resources/config +++ b/resources/config @@ -6,7 +6,7 @@ // 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", "keyboard_state", "sway/language", "battery", "battery#bat2", "clock", "tray"], + "modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "keyboard-state", "sway/language", "battery", "battery#bat2", "clock", "tray"], // Modules configuration // "sway/workspaces": { // "disable-scroll": true, @@ -23,7 +23,7 @@ // "default": "" // } // }, - "keyboard_state": { + "keyboard-state": { "numlock": true, "capslock": true, "format": "{name} {icon}", diff --git a/resources/style.css b/resources/style.css index c1ac5ad..b585e5f 100644 --- a/resources/style.css +++ b/resources/style.css @@ -229,7 +229,7 @@ label:focus { min-width: 16px; } -#keyboard_state { +#keyboard-state { background: #97e1ad; color: #000000; padding: 0 0px; @@ -237,10 +237,10 @@ label:focus { min-width: 16px; } -#keyboard_state > label { +#keyboard-state > label { padding: 0 5px; } -#keyboard_state > label.locked { +#keyboard-state > label.locked { background: rgba(0, 0, 0, 0.2); } diff --git a/src/factory.cpp b/src/factory.cpp index 6a0147d..9836354 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -71,7 +71,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { } #endif #ifdef HAVE_LIBEVDEV - if (ref == "keyboard_state") { + if (ref == "keyboard-state") { return new waybar::modules::KeyboardState(id, bar_, config_[name]); } #endif diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index ca72144..2b6eb2d 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -9,7 +9,7 @@ extern "C" { } waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config) - : AModule(config, "keyboard_state", id, false, !config["disable-scroll"].asBool()), + : AModule(config, "keyboard-state", id, false, !config["disable-scroll"].asBool()), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), numlock_label_(""), capslock_label_(""), @@ -31,7 +31,7 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& : "unlocked"), fd_(0), dev_(nullptr) { - box_.set_name("keyboard_state"); + box_.set_name("keyboard-state"); if (config_["numlock"].asBool()) { box_.pack_end(numlock_label_, false, false, 0); } From 68e4457f3a226c985bb48bf470f1a992e91cf688 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 24 Jul 2021 17:24:37 +0300 Subject: [PATCH 290/355] Add tooltip-formay --- include/modules/sway/language.hpp | 1 + man/waybar-sway-language.5.scd | 5 +++++ src/modules/sway/language.cpp | 15 ++++++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index fb4438b..b310b7f 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -48,6 +48,7 @@ class Language : public ALabel, public sigc::trackable { const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY; Layout layout_; + std::string tooltip_format_ = ""; std::map layouts_map_; XKBContext xkb_context_; bool is_variant_displayed; diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index f7f8830..60a18fc 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -17,6 +17,11 @@ Addressed by *sway/language* default: {} ++ The format, how layout should be displayed. +*format-tooltip*: ++ + typeof: string ++ + default: {} ++ + The format, how layout should be displayed in tooltip. + *tooltip*: ++ typeof: bool ++ default: true ++ diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 86a8e1e..5f3ce06 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -1,6 +1,7 @@ #include "modules/sway/language.hpp" #include +#include #include #include @@ -19,6 +20,9 @@ const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name Language::Language(const std::string& id, const Json::Value& config) : ALabel(config, "language", id, "{}", 0, true) { is_variant_displayed = format_.find("{variant}") != std::string::npos; + if (config.isMember("tooltip-format")) { + tooltip_format_ = config["tooltip-format"].asString(); + } ipc_.subscribe(R"(["input"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd)); @@ -90,7 +94,16 @@ auto Language::update() -> void { fmt::arg("variant", layout_.variant))); label_.set_markup(display_layout); if (tooltipEnabled()) { - label_.set_tooltip_markup(display_layout); + if (tooltip_format_ != "") { + auto tooltip_display_layout = trim(fmt::format(tooltip_format_, + fmt::arg("short", layout_.short_name), + fmt::arg("long", layout_.full_name), + fmt::arg("variant", layout_.variant))); + label_.set_tooltip_markup(tooltip_display_layout); + + } else { + label_.set_tooltip_markup(display_layout); + } } event_box_.show(); From af2113931a2b1fc8d331581bbaec2f747d158f04 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 24 Jul 2021 17:26:49 +0300 Subject: [PATCH 291/355] fix typo --- man/waybar-sway-language.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index 60a18fc..76bc720 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -17,7 +17,7 @@ Addressed by *sway/language* default: {} ++ The format, how layout should be displayed. -*format-tooltip*: ++ +*tooltip-format*: ++ typeof: string ++ default: {} ++ The format, how layout should be displayed in tooltip. From 710f933fa6165bf02a25f371496de76f1cdbbca6 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Sat, 31 Jul 2021 14:55:17 +0000 Subject: [PATCH 292/355] Don't start if graphical-session is not running Currently waybar _can_ try to start even if there's no graphical session (and no sway) running. Adding `Requisite=` prevents this. From `systemd.unit(5)`: Requisite= Similar to Requires=. However, if the units listed here are not started already, they will not be started and the starting of this unit will fail immediately. Requisite= does not imply an ordering dependency, even if both units are started in the same transaction. Hence this setting should usually be combined with After=, to ensure this unit is not started before the other unit. When Requisite=b.service is used on a.service, this dependency will show as RequisiteOf=a.service in property listing of b.service. RequisiteOf= dependency cannot be specified directly. --- resources/waybar.service.in | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/waybar.service.in b/resources/waybar.service.in index ef0d07e..81ac677 100644 --- a/resources/waybar.service.in +++ b/resources/waybar.service.in @@ -3,6 +3,7 @@ Description=Highly customizable Wayland bar for Sway and Wlroots based composito Documentation=https://github.com/Alexays/Waybar/wiki/ PartOf=graphical-session.target After=graphical-session.target +Requisite=graphical-session.target [Service] ExecStart=@prefix@/bin/waybar From 4f6a9b1bc29cc73400e6d2b7253ff1bc1f1ba595 Mon Sep 17 00:00:00 2001 From: Michael Swiger Date: Sat, 31 Jul 2021 16:14:02 -0700 Subject: [PATCH 293/355] Fix incorrect tray icon scaling --- include/modules/sni/item.hpp | 2 ++ src/modules/sni/item.cpp | 68 +++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index 1f1503e..1115145 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -66,7 +66,9 @@ class Item : public sigc::trackable { void updateImage(); Glib::RefPtr extractPixBuf(GVariant* variant); + Glib::RefPtr getIconPixbuf(); Glib::RefPtr getIconByName(const std::string& name, int size); + double getScaledIconSize(); static void onMenuDestroyed(Item* self, GObject* old_menu_pointer); void makeMenu(); bool handleClick(GdkEventButton* const& /*ev*/); diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 267cf63..d40f0dd 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -310,44 +310,41 @@ Glib::RefPtr Item::extractPixBuf(GVariant* variant) { } void Item::updateImage() { - auto scale_factor = image.get_scale_factor(); - auto scaled_icon_size = icon_size * scale_factor; + auto pixbuf = getIconPixbuf(); + auto scaled_icon_size = getScaledIconSize(); + + if (!pixbuf) { + pixbuf = getIconByName("image-missing", getScaledIconSize()); + } - image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU); - image.set_pixel_size(scaled_icon_size); - if (!icon_name.empty()) { - try { - // Try to find icons specified by path and filename + // If the loaded icon is not square, assume that the icon height should match the + // requested icon size, but the width is allowed to be different. As such, if the + // height of the image does not match the requested icon size, resize the icon such that + // the aspect ratio is maintained, but the height matches the requested icon size. + if (pixbuf->get_height() != scaled_icon_size) { + int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height(); + pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); + } + + auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, 0, image.get_window()); + image.set(surface); +} + +Glib::RefPtr Item::getIconPixbuf() { + try { + if (!icon_name.empty()) { std::ifstream temp(icon_name); if (temp.is_open()) { - 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 - // Keep the aspect ratio and scale to make the height equal to scaled_icon_size - // If people have non square icons, assume they want it to grow in width not height - int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height(); - - pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); - - auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, 0, image.get_window()); - image.set(surface); - } - } else { - auto icon_by_name = getIconByName(icon_name, scaled_icon_size); - auto surface = Gdk::Cairo::create_surface_from_pixbuf(icon_by_name, 0, image.get_window()); - image.set(surface); + return Gdk::Pixbuf::create_from_file(icon_name); } - } catch (Glib::Error& e) { - spdlog::error("Item '{}': {}", id, static_cast(e.what())); + return getIconByName(icon_name, getScaledIconSize()); + } else if (icon_pixmap) { + return icon_pixmap; } - } else if (icon_pixmap) { - // An icon extracted may be the wrong size for the tray - icon_pixmap = icon_pixmap->scale_simple(icon_size, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); - auto surface = Gdk::Cairo::create_surface_from_pixbuf(icon_pixmap, 0, image.get_window()); - image.set(icon_pixmap); - } + } catch (Glib::Error& e) { + spdlog::error("Item '{}': {}", id, static_cast(e.what())); + } + return getIconByName("image-missing", getScaledIconSize()); } Glib::RefPtr Item::getIconByName(const std::string& name, int request_size) { @@ -382,6 +379,11 @@ Glib::RefPtr Item::getIconByName(const std::string& name, int reque name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } +double Item::getScaledIconSize() { + // apply the scale factor from the Gtk window to the requested icon size + return icon_size * image.get_scale_factor(); +} + void Item::onMenuDestroyed(Item* self, GObject* old_menu_pointer) { if (old_menu_pointer == reinterpret_cast(self->dbus_menu)) { self->gtk_menu = nullptr; From e5787a2617df99ed465a46b5cd3063f3c440450a Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 16 Aug 2021 15:47:34 +0200 Subject: [PATCH 294/355] chore: 0.9.8 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 1c065f7..835b70e 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.7', + version: '0.9.8', license: 'MIT', meson_version: '>= 0.49.0', default_options : [ From a57e431437da8bae31cf4bde1ede57d7d21609b6 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 17 Aug 2021 05:28:41 +0300 Subject: [PATCH 295/355] Add shortDescription --- include/modules/sway/language.hpp | 3 ++- man/waybar-sway-language.5.scd | 6 ++++-- src/modules/sway/language.cpp | 21 ++++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index b310b7f..5cceee6 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -25,6 +25,7 @@ class Language : public ALabel, public sigc::trackable { std::string full_name; std::string short_name; std::string variant; + std::string short_description; }; class XKBContext { @@ -36,6 +37,7 @@ class Language : public ALabel, public sigc::trackable { rxkb_context* context_ = nullptr; rxkb_layout* xkb_layout_ = nullptr; Layout* layout_ = nullptr; + std::map base_layouts_by_name_; }; void onEvent(const struct Ipc::ipc_response&); @@ -50,7 +52,6 @@ class Language : public ALabel, public sigc::trackable { Layout layout_; std::string tooltip_format_ = ""; std::map layouts_map_; - XKBContext xkb_context_; bool is_variant_displayed; util::JsonParser parser_; diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index 76bc720..92a647e 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -29,11 +29,13 @@ Addressed by *sway/language* # FORMAT REPLACEMENTS -*{short}*: Short name of layout (e.g. "en"). Equals to {}. +*{short}*: Short name of layout (e.g. "us"). Equals to {}. + +*{shortDescription}*: Short description of layout (e.g. "en"). *{long}*: Long name of layout (e.g. "English (Dvorak)"). -*{variant}*: Variant of layout (e.g. "Dvorak"). +*{variant}*: Variant of layout (e.g. "dvorak"). # EXAMPLES diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 5f3ce06..f7be4cb 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -90,6 +90,7 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { auto Language::update() -> void { auto display_layout = trim(fmt::format(format_, fmt::arg("short", layout_.short_name), + fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), fmt::arg("variant", layout_.variant))); label_.set_markup(display_layout); @@ -97,10 +98,10 @@ auto Language::update() -> void { if (tooltip_format_ != "") { auto tooltip_display_layout = trim(fmt::format(tooltip_format_, fmt::arg("short", layout_.short_name), + fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), fmt::arg("variant", layout_.variant))); label_.set_tooltip_markup(tooltip_display_layout); - } else { label_.set_tooltip_markup(display_layout); } @@ -118,8 +119,9 @@ auto Language::set_current_layout(std::string current_layout) -> void { auto Language::init_layouts_map(const std::vector& used_layouts) -> void { std::map> found_by_short_names; - auto layout = xkb_context_.next_layout(); - for (; layout != nullptr; layout = xkb_context_.next_layout()) { + XKBContext* xkb_context_ = new XKBContext(); + auto layout = xkb_context_->next_layout(); + for (; layout != nullptr; layout = xkb_context_->next_layout()) { if (std::find(used_layouts.begin(), used_layouts.end(), layout->full_name) == used_layouts.end()) { continue; @@ -136,6 +138,7 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> layouts_map_.emplace(layout->full_name, *layout); } + //delete xkb_context_; if (is_variant_displayed || found_by_short_names.size() == 0) { return; @@ -145,7 +148,6 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> for (const auto& used_layout_name : used_layouts) { auto used_layout = &layouts_map_.find(used_layout_name)->second; auto layouts_with_same_name_list = found_by_short_names[used_layout->short_name]; - spdlog::info("SIZE: " + std::to_string(layouts_with_same_name_list.size())); if (layouts_with_same_name_list.size() < 2) { continue; } @@ -180,8 +182,17 @@ auto Language::XKBContext::next_layout() -> Layout* { auto name = std::string(rxkb_layout_get_name(xkb_layout_)); auto variant_ = rxkb_layout_get_variant(xkb_layout_); std::string variant = variant_ == nullptr ? "" : std::string(variant_); + auto short_description_ = rxkb_layout_get_brief(xkb_layout_); + std::string short_description; + if (short_description_ != nullptr) { + short_description = std::string(short_description_); + base_layouts_by_name_.emplace(name, xkb_layout_); + } else { + auto base_layout = base_layouts_by_name_[name]; + short_description = base_layout == nullptr ? "" : std::string(rxkb_layout_get_brief(base_layout)); + } - layout_ = new Layout{description, name, variant}; + layout_ = new Layout{description, name, variant, short_description}; return layout_; } From a87a967a97c48472a6b8bc632b0ad87c0988ac0d Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 17 Aug 2021 05:29:35 +0300 Subject: [PATCH 296/355] Fix leak --- src/modules/sway/language.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index f7be4cb..0b56dad 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -138,7 +138,7 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> layouts_map_.emplace(layout->full_name, *layout); } - //delete xkb_context_; + delete xkb_context_; if (is_variant_displayed || found_by_short_names.size() == 0) { return; From 2d80d3152797e53b2b7176e498d5c02f69b3cff8 Mon Sep 17 00:00:00 2001 From: Michael Swiger Date: Mon, 16 Aug 2021 23:33:29 -0700 Subject: [PATCH 297/355] Fix tray icon scaling on multi-display setups --- include/modules/sni/host.hpp | 5 ++++- include/modules/sni/item.hpp | 5 ++++- src/modules/sni/host.cpp | 5 +++-- src/modules/sni/item.cpp | 9 ++++++++- src/modules/sni/tray.cpp | 2 +- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/modules/sni/host.hpp b/include/modules/sni/host.hpp index f97900f..8d32103 100644 --- a/include/modules/sni/host.hpp +++ b/include/modules/sni/host.hpp @@ -5,13 +5,15 @@ #include #include #include +#include "bar.hpp" #include "modules/sni/item.hpp" namespace waybar::modules::SNI { class Host { public: - Host(const std::size_t id, const Json::Value&, const std::function&)>&, + Host(const std::size_t id, const Json::Value&, const Bar&, + const std::function&)>&, const std::function&)>&); ~Host(); @@ -36,6 +38,7 @@ class Host { GCancellable* cancellable_ = nullptr; SnWatcher* watcher_ = nullptr; const Json::Value& config_; + const Bar& bar_; const std::function&)> on_add_; const std::function&)> on_remove_; }; diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index 1115145..430c351 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -14,6 +14,8 @@ #include #include +#include "bar.hpp" + namespace waybar::modules::SNI { struct ToolTip { @@ -23,7 +25,7 @@ struct ToolTip { class Item : public sigc::trackable { public: - Item(const std::string&, const std::string&, const Json::Value&); + Item(const std::string&, const std::string&, const Json::Value&, const Bar&); ~Item() = default; std::string bus_name; @@ -56,6 +58,7 @@ class Item : public sigc::trackable { bool item_is_menu = true; private: + void onConfigure(GdkEventConfigure* ev); void proxyReady(Glib::RefPtr& result); void setProperty(const Glib::ustring& name, Glib::VariantBase& value); void setStatus(const Glib::ustring& value); diff --git a/src/modules/sni/host.cpp b/src/modules/sni/host.cpp index 868fcd6..414f151 100644 --- a/src/modules/sni/host.cpp +++ b/src/modules/sni/host.cpp @@ -4,7 +4,7 @@ namespace waybar::modules::SNI { -Host::Host(const std::size_t id, const Json::Value& config, +Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar, const std::function&)>& on_add, const std::function&)>& on_remove) : bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" + @@ -13,6 +13,7 @@ Host::Host(const std::size_t id, const Json::Value& config, bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION, bus_name_, sigc::mem_fun(*this, &Host::busAcquired))), config_(config), + bar_(bar), on_add_(on_add), on_remove_(on_remove) {} @@ -136,7 +137,7 @@ void Host::addRegisteredItem(std::string service) { 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_)); + items_.emplace_back(new Item(bus_name, object_path, config_, bar_)); on_add_(items_.back()); } } diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index d40f0dd..991ccc4 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -39,7 +39,7 @@ 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) +Item::Item(const std::string& bn, const std::string& op, const Json::Value& config, const Bar& bar) : bus_name(bn), object_path(op), icon_size(16), @@ -54,6 +54,9 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf if (config["show-passive-items"].isBool()) { show_passive_ = config["show-passive-items"].asBool(); } + + auto &window = const_cast(bar).window; + window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Item::onConfigure)); event_box.add(image); event_box.add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick)); @@ -73,6 +76,10 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf interface); } +void Item::onConfigure(GdkEventConfigure* ev) { + this->updateImage(); +} + void Item::proxyReady(Glib::RefPtr& result) { try { this->proxy_ = Gio::DBus::Proxy::create_for_bus_finish(result); diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index ae3702c..e73c9eb 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -7,7 +7,7 @@ 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_(SNI::Watcher::getInstance()), - host_(nb_hosts_, config, std::bind(&Tray::onAdd, this, std::placeholders::_1), + host_(nb_hosts_, config, bar, 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 " From 024fd42e272f79cb08e594eb3af86c93b1446c91 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 19 Aug 2021 14:59:25 +0200 Subject: [PATCH 298/355] river/tags: support urgent tags Upstream river has a concept of urgent views/tags as of commit e59c2a73. Introduce a new urgent style to expose this in the waybar module. --- include/modules/river/tags.hpp | 1 + man/waybar-river-tags.5.scd | 6 ++++-- protocol/river-status-unstable-v1.xml | 17 +++++++++++++---- src/modules/river/tags.cpp | 27 +++++++++++++++++++++++++-- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/include/modules/river/tags.hpp b/include/modules/river/tags.hpp index f80b3c5..9b75fbd 100644 --- a/include/modules/river/tags.hpp +++ b/include/modules/river/tags.hpp @@ -18,6 +18,7 @@ class Tags : public waybar::AModule { // Handlers for wayland events void handle_focused_tags(uint32_t tags); void handle_view_tags(struct wl_array *tags); + void handle_urgent_tags(uint32_t tags); struct zriver_status_manager_v1 *status_manager_; diff --git a/man/waybar-river-tags.5.scd b/man/waybar-river-tags.5.scd index 0f02724..65b9033 100644 --- a/man/waybar-river-tags.5.scd +++ b/man/waybar-river-tags.5.scd @@ -15,7 +15,7 @@ Addressed by *river/tags* *num-tags*: ++ typeof: uint ++ default: 9 ++ - The number of tags that should be displayed. + The number of tags that should be displayed. Max 32. *tag-labels*: ++ typeof: array ++ @@ -34,8 +34,10 @@ Addressed by *river/tags* - *#tags button* - *#tags button.occupied* - *#tags button.focused* +- *#tags button.urgent* -Note that a tag can be both occupied and focused at the same time. +Note that occupied/focused/urgent status may overlap. That is, a tag may be +both occupied and focused at the same time. # SEE ALSO diff --git a/protocol/river-status-unstable-v1.xml b/protocol/river-status-unstable-v1.xml index a4d6f4e..13affaa 100644 --- a/protocol/river-status-unstable-v1.xml +++ b/protocol/river-status-unstable-v1.xml @@ -1,7 +1,7 @@ - Copyright 2020 Isaac Freund + Copyright 2020 The River Developers Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -16,7 +16,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - + A global factory for objects that receive status information specific to river. It could be used to implement, for example, a status bar. @@ -47,7 +47,7 @@ - + This interface allows clients to receive information about the current windowing state of an output. @@ -75,12 +75,21 @@ + + + + Sent once on binding the interface and again whenever the set of + tags with at least one urgent view changes. + + + This interface allows clients to receive information about the current - focus of a seat. + focus of a seat. Note that (un)focused_output events will only be sent + if the client has bound the relevant wl_output globals. diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index e96b201..2628af2 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -22,14 +22,24 @@ static void listen_view_tags(void *data, struct zriver_output_status_v1 *zriver_ static_cast(data)->handle_view_tags(tags); } +static void listen_urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + uint32_t tags) { + static_cast(data)->handle_urgent_tags(tags); +} + static const zriver_output_status_v1_listener output_status_listener_impl{ .focused_tags = listen_focused_tags, .view_tags = listen_view_tags, + .urgent_tags = listen_urgent_tags, }; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (std::strcmp(interface, zriver_status_manager_v1_interface.name) == 0) { + version = std::min(version, 2); + if (version < ZRIVER_OUTPUT_STATUS_V1_URGENT_TAGS_SINCE_VERSION) { + spdlog::warn("river server does not support urgent tags"); + } static_cast(data)->status_manager_ = static_cast( wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, version)); } @@ -64,8 +74,9 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con } event_box_.add(box_); - // Default to 9 tags - const uint32_t num_tags = config["num-tags"].isUInt() ? config_["num-tags"].asUInt() : 9; + // Default to 9 tags, cap at 32 + const uint32_t num_tags = + config["num-tags"].isUInt() ? std::min(32, config_["num-tags"].asUInt()) : 9; std::vector tag_labels(num_tags); for (uint32_t tag = 0; tag < num_tags; ++tag) { @@ -129,4 +140,16 @@ void Tags::handle_view_tags(struct wl_array *view_tags) { } } +void Tags::handle_urgent_tags(uint32_t tags) { + uint32_t i = 0; + for (auto &button : buttons_) { + if ((1 << i) & tags) { + button.get_style_context()->add_class("urgent"); + } else { + button.get_style_context()->remove_class("urgent"); + } + ++i; + } +} + } /* namespace waybar::modules::river */ From c058a2d196509c15de6c85c241052f5ddc37c430 Mon Sep 17 00:00:00 2001 From: dmitry Date: Fri, 20 Aug 2021 01:09:16 +0300 Subject: [PATCH 299/355] Add number to shortDescripton --- include/modules/sway/language.hpp | 7 +++++++ src/modules/sway/language.cpp | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index 5cceee6..1faf52b 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -21,6 +21,12 @@ class Language : public ALabel, public sigc::trackable { auto update() -> void; private: + enum class DispayedShortFlag { + None = 0, + ShortName = 1, + ShortDescription = 1 << 1 + }; + struct Layout { std::string full_name; std::string short_name; @@ -53,6 +59,7 @@ class Language : public ALabel, public sigc::trackable { std::string tooltip_format_ = ""; std::map layouts_map_; bool is_variant_displayed; + std::byte displayed_short_flag = static_cast(DispayedShortFlag::None); util::JsonParser parser_; std::mutex mutex_; diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 0b56dad..c13c636 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -20,6 +20,12 @@ const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name Language::Language(const std::string& id, const Json::Value& config) : ALabel(config, "language", id, "{}", 0, true) { is_variant_displayed = format_.find("{variant}") != std::string::npos; + if (format_.find("{}") != std::string::npos || format_.find("{short}") != std::string::npos) { + displayed_short_flag |= static_cast(DispayedShortFlag::ShortName); + } + if (format_.find("{shortDescription}") != std::string::npos) { + displayed_short_flag |= static_cast(DispayedShortFlag::ShortDescription); + } if (config.isMember("tooltip-format")) { tooltip_format_ = config["tooltip-format"].asString(); } @@ -155,9 +161,15 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> if (short_name_to_number_map.count(used_layout->short_name) == 0) { short_name_to_number_map[used_layout->short_name] = 1; } - - used_layout->short_name = - used_layout->short_name + std::to_string(short_name_to_number_map[used_layout->short_name]++); + + if (displayed_short_flag != static_cast(0)) { + int& number = short_name_to_number_map[used_layout->short_name]; + used_layout->short_name = + used_layout->short_name + std::to_string(number); + used_layout->short_description = + used_layout->short_description + std::to_string(number); + ++number; + } } } From 9ee701974fc23f82fd0f719ae4b2f2b50aca4f62 Mon Sep 17 00:00:00 2001 From: Gavin Beatty Date: Fri, 20 Aug 2021 10:06:35 -0500 Subject: [PATCH 300/355] Fix memory leak and data race - Delete previous Layout before creating next one, and in destructor - Use stack XKBContext instead of local new+delete - Lock mutex in update() as it is called from a different thread than onEvent(res) --- src/modules/sway/language.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index c13c636..85dc769 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -94,6 +94,7 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { } auto Language::update() -> void { + std::lock_guard lock(mutex_); auto display_layout = trim(fmt::format(format_, fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), @@ -125,9 +126,9 @@ auto Language::set_current_layout(std::string current_layout) -> void { auto Language::init_layouts_map(const std::vector& used_layouts) -> void { std::map> found_by_short_names; - XKBContext* xkb_context_ = new XKBContext(); - auto layout = xkb_context_->next_layout(); - for (; layout != nullptr; layout = xkb_context_->next_layout()) { + XKBContext xkb_context; + auto layout = xkb_context.next_layout(); + for (; layout != nullptr; layout = xkb_context.next_layout()) { if (std::find(used_layouts.begin(), used_layouts.end(), layout->full_name) == used_layouts.end()) { continue; @@ -144,7 +145,6 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> layouts_map_.emplace(layout->full_name, *layout); } - delete xkb_context_; if (is_variant_displayed || found_by_short_names.size() == 0) { return; @@ -203,10 +203,13 @@ auto Language::XKBContext::next_layout() -> Layout* { auto base_layout = base_layouts_by_name_[name]; short_description = base_layout == nullptr ? "" : std::string(rxkb_layout_get_brief(base_layout)); } - + delete layout_; layout_ = new Layout{description, name, variant, short_description}; return layout_; } -Language::XKBContext::~XKBContext() { rxkb_context_unref(context_); } +Language::XKBContext::~XKBContext() { + rxkb_context_unref(context_); + delete layout_; +} } // namespace waybar::modules::sway From cb49650ea48b457e0b5a4572d794bbc1a73bc33a Mon Sep 17 00:00:00 2001 From: Michael Swiger Date: Sun, 22 Aug 2021 14:46:40 -0700 Subject: [PATCH 301/355] Use g_memdup2 instead of g_memdup This fixes a compile warning. See: https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538 --- src/modules/sni/item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 991ccc4..917a92a 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -287,7 +287,7 @@ Glib::RefPtr Item::extractPixBuf(GVariant* variant) { if (array != nullptr) { g_free(array); } - array = static_cast(g_memdup(data, size)); + array = static_cast(g_memdup2(data, size)); lwidth = width; lheight = height; } From 7b4b5e55a27ffbb748c63f2ebecab7d1629f7250 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Mon, 23 Aug 2021 07:30:07 +0200 Subject: [PATCH 302/355] support format-icon for cpu und memory --- man/waybar-cpu.5.scd | 5 +++++ man/waybar-memory.5.scd | 5 +++++ src/modules/cpu/common.cpp | 2 ++ src/modules/memory/common.cpp | 2 ++ 4 files changed, 14 insertions(+) diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index fbf6206..679ca2c 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -20,6 +20,11 @@ The *cpu* module displays the current cpu utilization. default: {usage}% ++ The format, how information should be displayed. On {} data gets inserted. +*format-icons*: ++ + typeof: array/object ++ + Based on the current usage, 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. diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index 3ff4c35..0639c07 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -22,6 +22,11 @@ Addressed by *memory* default: {percentage}% ++ The format, how information should be displayed. +*format-icons*: ++ + typeof: array/object ++ + Based on the current percentage, the corresponding icon gets selected. ++ + The order is *low* to *high*. Or by the state if it is an object. + *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index 2ca7421..767cde9 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -26,9 +26,11 @@ auto waybar::modules::Cpu::update() -> void { event_box_.hide(); } else { event_box_.show(); + auto icons = std::vector{state}; label_.set_markup(fmt::format(format, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage), + fmt::arg("icon", getIcon(cpu_usage, icons)), fmt::arg("max_frequency", max_frequency), fmt::arg("min_frequency", min_frequency), fmt::arg("avg_frequency", avg_frequency))); diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index 09ce8e8..31219ed 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -38,8 +38,10 @@ auto waybar::modules::Memory::update() -> void { event_box_.hide(); } else { event_box_.show(); + auto icons = std::vector{state}; label_.set_markup(fmt::format(format, used_ram_percentage, + fmt::arg("icon", getIcon(used_ram_percentage, icons)), fmt::arg("total", total_ram_gigabytes), fmt::arg("percentage", used_ram_percentage), fmt::arg("used", used_ram_gigabytes), From e0260ac4fc05b533210e35ef9b016ea3838f845b Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Mon, 23 Aug 2021 08:02:08 +0200 Subject: [PATCH 303/355] rm travis-ci shield --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 37c0cfc..98b99a2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 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) +# 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 From 9d9f95976926d5817837d03344b9506d700cee16 Mon Sep 17 00:00:00 2001 From: Ciaran Downey Date: Wed, 25 Aug 2021 14:47:51 -0700 Subject: [PATCH 304/355] Switch network module to read /proc/net/dev This fixes issue #610 by reading bandwidth usage per-interface from /proc/net/dev instead of globally via /proc/net/netstat. It supports the same matching logic as elsewhere, so setting interface to '*' should display the same sum-total bandwidth usage as the previous implementation. --- include/modules/network.hpp | 1 + src/modules/network.cpp | 168 +++++++++++++++++------------------- 2 files changed, 79 insertions(+), 90 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 009ae5a..183c9e2 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -43,6 +43,7 @@ class Network : public ALabel { const std::string getNetworkState() const; void clearIface(); bool wildcardMatch(const std::string& pattern, const std::string& text) const; + std::optional> readBandwidthUsage(); int ifid_; sa_family_t family_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 7d0f638..22b9f71 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -1,87 +1,79 @@ -#include "modules/network.hpp" +#include #include #include -#include -#include + #include +#include +#include #include + +#include "modules/network.hpp" #include "util/format.hpp" #ifdef WANT_RFKILL #include "util/rfkill.hpp" #endif 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"; constexpr const char *DEFAULT_FORMAT = "{ifname}"; - -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 +constexpr const char *NETDEV_FILE = + "/proc/net/dev"; // std::ifstream does not take std::string_view as param +std::optional> +waybar::modules::Network::readBandwidthUsage() { + std::ifstream netdev(NETDEV_FILE); + if (!netdev) { + spdlog::warn("Failed to open netdev file {}", NETDEV_FILE); + return {}; + } + + // skip the headers + std::string line; + std::getline(netdev, line); + std::getline(netdev, line); + if (!netdev) { + spdlog::warn("Unexpectedly short netdev file {}", NETDEV_FILE); + return {}; + } + + unsigned long long receivedBytes = 0ull; + unsigned long long transmittedBytes = 0ull; + for (std::getline(netdev, line); netdev; std::getline(netdev, line)) { + std::istringstream iss(line); + + std::string ifacename; + iss >> ifacename; // ifacename contains "eth0:" + ifacename.pop_back(); // remove trailing ':' + if (!checkInterface(ifacename)) { + continue; + } + + // The rest of the line consists of whitespace separated counts divided + // into two groups (receive and transmit). The first column in each group + // is bytes, which is the only one we care about. + unsigned long long r = 0ull; + unsigned long long t = 0ull; + // Read received bytes + iss >> r; + // Skip all the other columns in the received group + for (int colsToSkip = 7; colsToSkip > 0; colsToSkip--) { + // skip whitespace between columns + while (iss.peek() == ' ') { iss.ignore(); } + // skip the irrelevant column + while (iss.peek() != ' ') { iss.ignore(); } + } + // Read transmit bytes + iss >> t; + spdlog::trace("read r={}, t={}, iface={}", r, t, ifacename); + + receivedBytes += r; + transmittedBytes += t; + } + + return {{receivedBytes, transmittedBytes}}; +} + waybar::modules::Network::Network(const std::string &id, const Json::Value &config) : ALabel(config, "network", id, DEFAULT_FORMAT, 60), ifid_(-1), @@ -106,17 +98,12 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf // the module start with no text, but the the event_box_ is shown. label_.set_markup(""); - 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; + auto bandwidth = readBandwidthUsage(); + if (bandwidth.has_value()) { + bandwidth_down_total_ = (*bandwidth).first; + bandwidth_up_total_ = (*bandwidth).second; } else { bandwidth_down_total_ = 0; - } - - if (up_octets) { - bandwidth_up_total_ = *up_octets; - } else { bandwidth_up_total_ = 0; } @@ -303,20 +290,21 @@ const std::string waybar::modules::Network::getNetworkState() const { 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; + auto bandwidth = readBandwidthUsage(); + auto bandwidth_down = 0ull; + auto bandwidth_up = 0ull; + if (bandwidth.has_value()) { + auto down_octets = (*bandwidth).first; + auto up_octets = (*bandwidth).second; + + bandwidth_down = down_octets - bandwidth_down_total_; + bandwidth_down_total_ = down_octets; + + bandwidth_up = up_octets - bandwidth_up_total_; + bandwidth_up_total_ = up_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_)) { From 5186dd27e64b00b19736a2fa3bd6b325c4fcd555 Mon Sep 17 00:00:00 2001 From: Ciaran Downey Date: Thu, 26 Aug 2021 11:30:06 -0700 Subject: [PATCH 305/355] Use while (getline) instead of a for loop Also make the comments surrounding the /proc/net/dev parsing clearer and remove the apparently redundant "is the netdev file still good?" check. --- src/modules/network.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 22b9f71..5818f02 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -28,18 +28,14 @@ waybar::modules::Network::readBandwidthUsage() { return {}; } - // skip the headers std::string line; + // skip the headers (first two lines) std::getline(netdev, line); std::getline(netdev, line); - if (!netdev) { - spdlog::warn("Unexpectedly short netdev file {}", NETDEV_FILE); - return {}; - } unsigned long long receivedBytes = 0ull; unsigned long long transmittedBytes = 0ull; - for (std::getline(netdev, line); netdev; std::getline(netdev, line)) { + while (std::getline(netdev, line)) { std::istringstream iss(line); std::string ifacename; @@ -50,8 +46,11 @@ waybar::modules::Network::readBandwidthUsage() { } // The rest of the line consists of whitespace separated counts divided - // into two groups (receive and transmit). The first column in each group - // is bytes, which is the only one we care about. + // into two groups (receive and transmit). Each group has the following + // columns: bytes, packets, errs, drop, fifo, frame, compressed, multicast + // + // We only care about the bytes count, so we'll just ignore the 7 other + // columns. unsigned long long r = 0ull; unsigned long long t = 0ull; // Read received bytes @@ -65,7 +64,6 @@ waybar::modules::Network::readBandwidthUsage() { } // Read transmit bytes iss >> t; - spdlog::trace("read r={}, t={}, iface={}", r, t, ifacename); receivedBytes += r; transmittedBytes += t; From 4f76c9bd43dd7ff8108ffe81c690fe3e6232ee6a Mon Sep 17 00:00:00 2001 From: Michael Swiger Date: Sun, 29 Aug 2021 13:11:04 -0700 Subject: [PATCH 306/355] Only use g_memdup2 for glib >= 2.68 --- src/modules/sni/item.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 917a92a..d9748ed 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -287,7 +287,11 @@ Glib::RefPtr Item::extractPixBuf(GVariant* variant) { if (array != nullptr) { g_free(array); } +#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 68 array = static_cast(g_memdup2(data, size)); +#else + array = static_cast(g_memdup(data, size)); +#endif lwidth = width; lheight = height; } From aacd0fcc652f36901632a252bd91e7f0c5f73cf1 Mon Sep 17 00:00:00 2001 From: Matan1x Date: Wed, 8 Sep 2021 17:12:30 +0300 Subject: [PATCH 307/355] round brightness --- src/modules/backlight.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 3ebc6e7..fcd668c 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -173,7 +173,7 @@ auto waybar::modules::Backlight::update() -> void { return; } - const auto percent = best->get_max() == 0 ? 100 : best->get_actual() * 100 / best->get_max(); + const uint8_t percent = best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max()); label_.set_markup(fmt::format( format_, fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent)))); getState(percent); From 2c380a53caabfc831d832985becb95b69d202eed Mon Sep 17 00:00:00 2001 From: Rolf Vidar Mazunki Hoksaas Date: Thu, 9 Sep 2021 20:05:18 +0200 Subject: [PATCH 308/355] added support for the {gwaddr} variable --- Makefile | 3 +++ include/modules/network.hpp | 1 + man/waybar-network.5.scd | 2 ++ src/modules/network.cpp | 10 +++++++++- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d7182c1..ccf0507 100644 --- a/Makefile +++ b/Makefile @@ -16,5 +16,8 @@ install: build run: build ./build/waybar +debug-run: build + ./build/waybar --log-level debug + clean: rm -rf build diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 009ae5a..ad28520 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -67,6 +67,7 @@ class Network : public ALabel { bool carrier_; std::string ifname_; std::string ipaddr_; + std::string gwaddr_; std::string netmask_; int cidr_; int32_t signal_strength_dbm_; diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index f274881..f8bdd65 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -131,6 +131,8 @@ Addressed by *network* *{ipaddr}*: The first IP of the interface. +*{gwaddr}*: The default gateway for the interface + *{netmask}*: The subnetmask corresponding to the IP. *{cidr}*: The subnetmask corresponding to the IP in CIDR notation. diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 7d0f638..a45f2b8 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -348,6 +348,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), + fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), fmt::arg("frequency", frequency_), fmt::arg("icon", getIcon(signal_strength_, state_)), @@ -376,6 +377,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), + fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), fmt::arg("frequency", frequency_), fmt::arg("icon", getIcon(signal_strength_, state_)), @@ -409,6 +411,7 @@ void waybar::modules::Network::clearIface() { ifname_.clear(); essid_.clear(); ipaddr_.clear(); + gwaddr_.clear(); netmask_.clear(); carrier_ = false; cidr_ = 0; @@ -581,6 +584,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { break; } + char temp_gw_addr[INET6_ADDRSTRLEN]; case RTM_DELROUTE: is_del_event = true; case RTM_NEWROUTE: { @@ -595,6 +599,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { int temp_idx = -1; uint32_t priority = 0; + /* Find the message(s) concerting the main routing table, each message * corresponds to a single routing table entry. */ @@ -612,9 +617,10 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { case RTA_GATEWAY: /* The gateway of the route. * - * If someone every needs to figure out the gateway address as well, + * If someone ever needs to figure out the gateway address as well, * it's here as the attribute payload. */ + inet_ntop(net->family_, RTA_DATA(attr), temp_gw_addr, sizeof(temp_gw_addr)); has_gateway = true; break; case RTA_DST: { @@ -655,6 +661,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->clearIface(); net->ifid_ = temp_idx; net->route_priority = priority; + net->gwaddr_ = temp_gw_addr; + spdlog::debug("netwok: gateway {}", net->gwaddr_); spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority); From 95ecff05511f71fc542429059b843ecd369854ef Mon Sep 17 00:00:00 2001 From: Rolf Vidar Mazunki Hoksaas Date: Thu, 9 Sep 2021 20:12:20 +0200 Subject: [PATCH 309/355] added example tooltip usage --- resources/config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/config b/resources/config index 87f24c0..b315721 100644 --- a/resources/config +++ b/resources/config @@ -117,7 +117,8 @@ "network": { // "interface": "wlp2*", // (Optional) To force the use of this interface "format-wifi": "{essid} ({signalStrength}%) ", - "format-ethernet": "{ifname}: {ipaddr}/{cidr} ", + "format-ethernet": "{ipaddr}/{cidr} ", + "tooltip-format": "{ifname} via {gwaddr} ", "format-linked": "{ifname} (No IP) ", "format-disconnected": "Disconnected ⚠", "format-alt": "{ifname}: {ipaddr}/{cidr}" From b377520a38dd0a2aa68301039e9f649d98a94f5f Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 06:08:27 -0700 Subject: [PATCH 310/355] refactor(client): extract config handling into a new class --- include/client.hpp | 14 +--- include/config.hpp | 31 +++++++++ meson.build | 1 + src/client.cpp | 148 +--------------------------------------- src/config.cpp | 166 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 156 deletions(-) create mode 100644 include/config.hpp create mode 100644 src/config.cpp diff --git a/include/client.hpp b/include/client.hpp index e7fa1db..e68e4ad 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -3,11 +3,10 @@ #include #include #include -#include #include -#include #include "bar.hpp" +#include "config.hpp" struct zwlr_layer_shell_v1; struct zwp_idle_inhibitor_v1; @@ -32,15 +31,8 @@ class Client { 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 bindInterfaces(); void handleOutput(struct waybar_output &output); - bool isValidOutput(const Json::Value &config, struct waybar_output &output); - auto setupConfig(const std::string &config_file, int depth) -> void; - auto resolveConfigIncludes(Json::Value &config, int depth) -> void; - auto mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void; auto setupCss(const std::string &css_file) -> void; struct waybar_output & getOutput(void *); std::vector getOutputConfigs(struct waybar_output &output); @@ -55,7 +47,7 @@ class Client { void handleMonitorRemoved(Glib::RefPtr monitor); void handleDeferredMonitorRemoval(Glib::RefPtr monitor); - Json::Value config_; + Config config_; Glib::RefPtr style_context_; Glib::RefPtr css_provider_; std::list outputs_; diff --git a/include/config.hpp b/include/config.hpp new file mode 100644 index 0000000..bb7b906 --- /dev/null +++ b/include/config.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include + +namespace waybar { + +class Config { + public: + Config() = default; + + void load(const std::string &config, const std::string &style); + + const std::string &getStyle() { return css_file_; } + + Json::Value &getConfig() { return config_; } + + std::vector getOutputConfigs(const std::string &name, const std::string &identifier); + + private: + void setupConfig(const std::string &config_file, int depth); + void resolveConfigIncludes(Json::Value &config, int depth); + void mergeConfig(Json::Value &a_config_, Json::Value &b_config_); + + std::string config_file_; + std::string css_file_; + + Json::Value config_; +}; +} // namespace waybar diff --git a/meson.build b/meson.build index 835b70e..641607d 100644 --- a/meson.build +++ b/meson.build @@ -149,6 +149,7 @@ src_files = files( 'src/main.cpp', 'src/bar.cpp', 'src/client.cpp', + 'src/config.cpp', 'src/util/ustring_clen.cpp' ) diff --git a/src/client.cpp b/src/client.cpp index ff6e7bf..b50faff 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -3,12 +3,10 @@ #include #include -#include #include #include "idle-inhibit-unstable-v1-client-protocol.h" #include "util/clara.hpp" -#include "util/json.hpp" #include "wlr-layer-shell-unstable-v1-client-protocol.h" waybar::Client *waybar::Client::inst() { @@ -16,23 +14,6 @@ waybar::Client *waybar::Client::inst() { 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); @@ -70,29 +51,6 @@ void waybar::Client::handleOutput(struct waybar_output &output) { zxdg_output_v1_add_listener(output.xdg_output.get(), &xdgOutputListener, &output); } -bool waybar::Client::isValidOutput(const Json::Value &config, struct waybar_output &output) { - if (config["output"].isArray()) { - for (auto const &output_conf : config["output"]) { - if (output_conf.isString() && - (output_conf.asString() == output.name || output_conf.asString() == output.identifier)) { - return true; - } - } - return false; - } else if (config["output"].isString()) { - auto config_output = config["output"].asString(); - if (!config_output.empty()) { - if (config_output.substr(0, 1) == "!") { - return config_output.substr(1) != output.name && - config_output.substr(1) != output.identifier; - } - return config_output == output.name || config_output == output.identifier; - } - } - - return true; -} - struct waybar::waybar_output &waybar::Client::getOutput(void *addr) { auto it = std::find_if( outputs_.begin(), outputs_.end(), [&addr](const auto &output) { return &output == addr; }); @@ -103,17 +61,7 @@ struct waybar::waybar_output &waybar::Client::getOutput(void *addr) { } std::vector waybar::Client::getOutputConfigs(struct waybar_output &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; + return config_.getOutputConfigs(output.name, output.identifier); } void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_output*/) { @@ -203,95 +151,6 @@ void waybar::Client::handleDeferredMonitorRemoval(Glib::RefPtr mon outputs_.remove_if([&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", - "$XDG_CONFIG_HOME/waybar/config.jsonc", - "$HOME/.config/waybar/config", - "$HOME/.config/waybar/config.jsonc", - "$HOME/waybar/config", - "$HOME/waybar/config.jsonc", - "/etc/xdg/waybar/config", - "/etc/xdg/waybar/config.jsonc", - SYSCONFDIR "/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", - SYSCONFDIR "/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, int depth) -> void { - if (depth > 100) { - throw std::runtime_error("Aborting due to likely recursive include in config files"); - } - 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; - Json::Value tmp_config_ = parser.parse(str); - if (tmp_config_.isArray()) { - for (auto &config_part : tmp_config_) { - resolveConfigIncludes(config_part, depth); - } - } else { - resolveConfigIncludes(tmp_config_, depth); - } - mergeConfig(config_, tmp_config_); -} - -auto waybar::Client::resolveConfigIncludes(Json::Value &config, int depth) -> void { - Json::Value includes = config["include"]; - if (includes.isArray()) { - for (const auto &include : includes) { - spdlog::info("Including resource file: {}", include.asString()); - setupConfig(getValidPath({include.asString()}), ++depth); - } - } else if (includes.isString()) { - spdlog::info("Including resource file: {}", includes.asString()); - setupConfig(getValidPath({includes.asString()}), ++depth); - } -} - -auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { - if (!a_config_) { - // For the first config - a_config_ = b_config_; - } else if (a_config_.isObject() && b_config_.isObject()) { - for (const auto &key : b_config_.getMemberNames()) { - if (a_config_[key].isObject() && b_config_[key].isObject()) { - mergeConfig(a_config_[key], b_config_[key]); - } else { - a_config_[key] = b_config_[key]; - } - } - } else if (a_config_.isArray() && b_config_.isArray()) { - // This can happen only on the top-level array of a multi-bar config - for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) { - if (a_config_[i].isObject() && b_config_[i].isObject()) { - mergeConfig(a_config_[i], b_config_[i]); - } - } - } else { - spdlog::error("Cannot merge config, conflicting or invalid JSON types"); - } -} - auto waybar::Client::setupCss(const std::string &css_file) -> void { css_provider_ = Gtk::CssProvider::create(); style_context_ = Gtk::StyleContext::create(); @@ -367,9 +226,8 @@ int waybar::Client::main(int argc, char *argv[]) { 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, 0); - setupCss(css_file); + config_.load(config, style); + setupCss(config_.getStyle()); bindInterfaces(); gtk_app->hold(); gtk_app->run(); diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..6c7fa17 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,166 @@ +#include "config.hpp" + +#include +#include +#include + +#include +#include + +#include "util/json.hpp" + +#ifndef SYSCONFDIR +#define SYSCONFDIR "/etc" +#endif + +namespace waybar { + +const std::string getValidPath(const std::vector &paths) { + 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(); +} + +std::tuple getConfigs(const std::string &config, + const std::string &style) { + auto config_file = config.empty() ? getValidPath({ + "$XDG_CONFIG_HOME/waybar/config", + "$XDG_CONFIG_HOME/waybar/config.jsonc", + "$HOME/.config/waybar/config", + "$HOME/.config/waybar/config.jsonc", + "$HOME/waybar/config", + "$HOME/waybar/config.jsonc", + "/etc/xdg/waybar/config", + "/etc/xdg/waybar/config.jsonc", + SYSCONFDIR "/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", + SYSCONFDIR "/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}; +} + +void Config::setupConfig(const std::string &config_file, int depth) { + if (depth > 100) { + throw std::runtime_error("Aborting due to likely recursive include in config files"); + } + 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; + Json::Value tmp_config = parser.parse(str); + if (tmp_config.isArray()) { + for (auto &config_part : tmp_config) { + resolveConfigIncludes(config_part, depth); + } + } else { + resolveConfigIncludes(tmp_config, depth); + } + mergeConfig(config_, tmp_config); +} + +void Config::resolveConfigIncludes(Json::Value &config, int depth) { + Json::Value includes = config["include"]; + if (includes.isArray()) { + for (const auto &include : includes) { + spdlog::info("Including resource file: {}", include.asString()); + setupConfig(getValidPath({include.asString()}), ++depth); + } + } else if (includes.isString()) { + spdlog::info("Including resource file: {}", includes.asString()); + setupConfig(getValidPath({includes.asString()}), ++depth); + } +} + +void Config::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) { + if (!a_config_) { + // For the first config + a_config_ = b_config_; + } else if (a_config_.isObject() && b_config_.isObject()) { + for (const auto &key : b_config_.getMemberNames()) { + if (a_config_[key].isObject() && b_config_[key].isObject()) { + mergeConfig(a_config_[key], b_config_[key]); + } else { + a_config_[key] = b_config_[key]; + } + } + } else if (a_config_.isArray() && b_config_.isArray()) { + // This can happen only on the top-level array of a multi-bar config + for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) { + if (a_config_[i].isObject() && b_config_[i].isObject()) { + mergeConfig(a_config_[i], b_config_[i]); + } + } + } else { + spdlog::error("Cannot merge config, conflicting or invalid JSON types"); + } +} +bool isValidOutput(const Json::Value &config, const std::string &name, + const std::string &identifier) { + if (config["output"].isArray()) { + for (auto const &output_conf : config["output"]) { + if (output_conf.isString() && + (output_conf.asString() == name || output_conf.asString() == identifier)) { + return true; + } + } + return false; + } else if (config["output"].isString()) { + auto config_output = config["output"].asString(); + if (!config_output.empty()) { + if (config_output.substr(0, 1) == "!") { + return config_output.substr(1) != name && config_output.substr(1) != identifier; + } + return config_output == name || config_output == identifier; + } + } + + return true; +} + +void Config::load(const std::string &config, const std::string &style) { + std::tie(config_file_, css_file_) = getConfigs(config, style); + setupConfig(config_file_, 0); +} + +std::vector Config::getOutputConfigs(const std::string &name, + const std::string &identifier) { + std::vector configs; + if (config_.isArray()) { + for (auto const &config : config_) { + if (config.isObject() && isValidOutput(config, name, identifier)) { + configs.push_back(config); + } + } + } else if (isValidOutput(config_, name, identifier)) { + configs.push_back(config_); + } + return configs; +} + +} // namespace waybar From 4fff2eaaa0851c559fabd5b04ef08de8b9604fe1 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 15 Sep 2021 21:24:45 +0700 Subject: [PATCH 311/355] refactor(client): change config visibility to public --- include/client.hpp | 2 +- src/client.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index e68e4ad..1124cbb 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -28,6 +28,7 @@ class Client { struct zxdg_output_manager_v1 * xdg_output_manager = nullptr; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr; std::vector> bars; + Config config; private: Client() = default; @@ -47,7 +48,6 @@ class Client { void handleMonitorRemoved(Glib::RefPtr monitor); void handleDeferredMonitorRemoval(Glib::RefPtr monitor); - Config config_; Glib::RefPtr style_context_; Glib::RefPtr css_provider_; std::list outputs_; diff --git a/src/client.cpp b/src/client.cpp index b50faff..0764883 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -61,7 +61,7 @@ struct waybar::waybar_output &waybar::Client::getOutput(void *addr) { } std::vector waybar::Client::getOutputConfigs(struct waybar_output &output) { - return config_.getOutputConfigs(output.name, output.identifier); + return config.getOutputConfigs(output.name, output.identifier); } void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_output*/) { @@ -188,14 +188,14 @@ void waybar::Client::bindInterfaces() { int waybar::Client::main(int argc, char *argv[]) { bool show_help = false; bool show_version = false; - std::string config; - std::string style; + std::string config_opt; + std::string style_opt; 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(config_opt, "config")["-c"]["--config"]("Config path") | + clara::detail::Opt(style_opt, "style")["-s"]["--style"]("Style path") | clara::detail::Opt( log_level, "trace|debug|info|warning|error|critical|off")["-l"]["--log-level"]("Log level") | @@ -226,8 +226,8 @@ int waybar::Client::main(int argc, char *argv[]) { throw std::runtime_error("Bar need to run under Wayland"); } wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj()); - config_.load(config, style); - setupCss(config_.getStyle()); + config.load(config_opt, style_opt); + setupCss(config.getStyle()); bindInterfaces(); gtk_app->hold(); gtk_app->run(); From 1f7d399b8edd3ea76612e674ee1b3fb1a365e483 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 07:19:47 -0700 Subject: [PATCH 312/355] refactor(config): remove style handling from Config --- include/client.hpp | 1 + include/config.hpp | 16 ++++++--- src/client.cpp | 14 ++++++-- src/config.cpp | 84 ++++++++++++++++++++++------------------------ 4 files changed, 66 insertions(+), 49 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index 1124cbb..bd80d0b 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -32,6 +32,7 @@ class Client { private: Client() = default; + const std::string getStyle(const std::string &style); void bindInterfaces(); void handleOutput(struct waybar_output &output); auto setupCss(const std::string &css_file) -> void; diff --git a/include/config.hpp b/include/config.hpp index bb7b906..25b78ab 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -2,17 +2,26 @@ #include +#include #include +#ifndef SYSCONFDIR +#define SYSCONFDIR "/etc" +#endif + namespace waybar { class Config { public: + static const std::vector CONFIG_DIRS; + + /* Try to find any of provided names in the supported set of config directories */ + static std::optional findConfigPath( + const std::vector &names, const std::vector &dirs = CONFIG_DIRS); + Config() = default; - void load(const std::string &config, const std::string &style); - - const std::string &getStyle() { return css_file_; } + void load(const std::string &config); Json::Value &getConfig() { return config_; } @@ -24,7 +33,6 @@ class Config { void mergeConfig(Json::Value &a_config_, Json::Value &b_config_); std::string config_file_; - std::string css_file_; Json::Value config_; }; diff --git a/src/client.cpp b/src/client.cpp index 0764883..95f5a29 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -151,6 +151,15 @@ void waybar::Client::handleDeferredMonitorRemoval(Glib::RefPtr mon outputs_.remove_if([&monitor](const auto &output) { return output.monitor == monitor; }); } +const std::string waybar::Client::getStyle(const std::string &style) { + auto css_file = style.empty() ? Config::findConfigPath({"style.css"}) : style; + if (!css_file) { + throw std::runtime_error("Missing required resource files"); + } + spdlog::info("Using CSS file {}", css_file.value()); + return css_file.value(); +}; + auto waybar::Client::setupCss(const std::string &css_file) -> void { css_provider_ = Gtk::CssProvider::create(); style_context_ = Gtk::StyleContext::create(); @@ -226,8 +235,9 @@ int waybar::Client::main(int argc, char *argv[]) { throw std::runtime_error("Bar need to run under Wayland"); } wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj()); - config.load(config_opt, style_opt); - setupCss(config.getStyle()); + config.load(config_opt); + auto css_file = getStyle(style_opt); + setupCss(css_file); bindInterfaces(); gtk_app->hold(); gtk_app->run(); diff --git a/src/config.cpp b/src/config.cpp index 6c7fa17..ed7168d 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -9,58 +9,51 @@ #include "util/json.hpp" -#ifndef SYSCONFDIR -#define SYSCONFDIR "/etc" -#endif - namespace waybar { -const std::string getValidPath(const std::vector &paths) { - wordexp_t p; +const std::vector Config::CONFIG_DIRS = { + "$XDG_CONFIG_HOME/waybar/", + "$HOME/.config/waybar/", + "$HOME/waybar/", + "/etc/xdg/waybar/", + SYSCONFDIR "/xdg/waybar/", + "./resources/", +}; - 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; - } +std::optional tryExpandPath(const std::string &path) { + wordexp_t p; + 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::nullopt; +} + +const std::string getValidPath(const std::vector &paths) { + for (const std::string &path : paths) { + if (auto res = tryExpandPath(path); res) { + return res.value(); } } return std::string(); } -std::tuple getConfigs(const std::string &config, - const std::string &style) { - auto config_file = config.empty() ? getValidPath({ - "$XDG_CONFIG_HOME/waybar/config", - "$XDG_CONFIG_HOME/waybar/config.jsonc", - "$HOME/.config/waybar/config", - "$HOME/.config/waybar/config.jsonc", - "$HOME/waybar/config", - "$HOME/waybar/config.jsonc", - "/etc/xdg/waybar/config", - "/etc/xdg/waybar/config.jsonc", - SYSCONFDIR "/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", - SYSCONFDIR "/xdg/waybar/style.css", - "./resources/style.css", - }) - : style; - if (css_file.empty() || config_file.empty()) { - throw std::runtime_error("Missing required resources files"); +std::optional Config::findConfigPath(const std::vector &names, + const std::vector &dirs) { + std::vector paths; + for (const auto &dir : dirs) { + for (const auto &name : names) { + if (auto res = tryExpandPath(dir + name); res) { + return res; + } + } } - spdlog::info("Resources files: {}, {}", config_file, css_file); - return {config_file, css_file}; + return std::nullopt; } void Config::setupConfig(const std::string &config_file, int depth) { @@ -143,8 +136,13 @@ bool isValidOutput(const Json::Value &config, const std::string &name, return true; } -void Config::load(const std::string &config, const std::string &style) { - std::tie(config_file_, css_file_) = getConfigs(config, style); +void Config::load(const std::string &config) { + auto file = config.empty() ? findConfigPath({"config", "config.jsonc"}) : config; + if (!file) { + throw std::runtime_error("Missing required resource files"); + } + config_file_ = file.value(); + spdlog::info("Using configuration file {}", config_file_); setupConfig(config_file_, 0); } From 1f16d7955d7832e2fcdc3317d42034ac0c772dac Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 08:19:12 -0700 Subject: [PATCH 313/355] refactor(config): drop getValidPath --- src/config.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index ed7168d..207e1bd 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -33,16 +33,6 @@ std::optional tryExpandPath(const std::string &path) { return std::nullopt; } -const std::string getValidPath(const std::vector &paths) { - for (const std::string &path : paths) { - if (auto res = tryExpandPath(path); res) { - return res.value(); - } - } - - return std::string(); -} - std::optional Config::findConfigPath(const std::vector &names, const std::vector &dirs) { std::vector paths; @@ -82,11 +72,11 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) { if (includes.isArray()) { for (const auto &include : includes) { spdlog::info("Including resource file: {}", include.asString()); - setupConfig(getValidPath({include.asString()}), ++depth); + setupConfig(tryExpandPath(include.asString()).value_or(""), ++depth); } } else if (includes.isString()) { spdlog::info("Including resource file: {}", includes.asString()); - setupConfig(getValidPath({includes.asString()}), ++depth); + setupConfig(tryExpandPath(includes.asString()).value_or(""), ++depth); } } From 6eba62f0600379f9d594dea1fa5c41024f55fe8e Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 18:33:24 -0700 Subject: [PATCH 314/355] test: add build configs for catch2 --- meson.build | 9 +++++++++ meson_options.txt | 1 + subprojects/catch2.wrap | 12 ++++++++++++ test/config.cpp | 4 ++++ test/meson.build | 21 +++++++++++++++++++++ 5 files changed, 47 insertions(+) create mode 100644 subprojects/catch2.wrap create mode 100644 test/config.cpp create mode 100644 test/meson.build diff --git a/meson.build b/meson.build index 641607d..c09fbb6 100644 --- a/meson.build +++ b/meson.build @@ -360,6 +360,15 @@ if scdoc.found() endforeach endif +catch2 = dependency( + 'catch2', + fallback: ['catch2', 'catch2_dep'], + required: get_option('tests'), +) +if catch2.found() + subdir('test') +endif + clangtidy = find_program('clang-tidy', required: false) if clangtidy.found() diff --git a/meson_options.txt b/meson_options.txt index fefb3dc..81e4468 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -10,3 +10,4 @@ option('mpd', type: 'feature', value: 'auto', description: 'Enable support for t option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support') option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL') option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio') +option('tests', type: 'feature', value: 'auto', description: 'Enable tests') diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap new file mode 100644 index 0000000..356c406 --- /dev/null +++ b/subprojects/catch2.wrap @@ -0,0 +1,12 @@ +[wrap-file] +directory = Catch2-2.13.3 +source_url = https://github.com/catchorg/Catch2/archive/v2.13.3.zip +source_filename = Catch2-2.13.3.zip +source_hash = 1804feb72bc15c0856b4a43aa586c661af9c3685a75973b6a8fc0b950c7cfd13 +patch_url = https://github.com/mesonbuild/catch2/releases/download/2.13.3-2/catch2.zip +patch_filename = catch2-2.13.3-2-wrap.zip +patch_hash = 21b590ab8c65b593ad5ee8f8e5b822bf9877b2c2672f97fbb52459751053eadf + +[provide] +catch2 = catch2_dep + diff --git a/test/config.cpp b/test/config.cpp new file mode 100644 index 0000000..41180bb --- /dev/null +++ b/test/config.cpp @@ -0,0 +1,4 @@ +#define CATCH_CONFIG_MAIN +#include "config.hpp" + +#include diff --git a/test/meson.build b/test/meson.build new file mode 100644 index 0000000..85b9771 --- /dev/null +++ b/test/meson.build @@ -0,0 +1,21 @@ +test_inc = include_directories('../include') +test_dep = [ + catch2, + fmt, + jsoncpp, + spdlog, +] + +config_test = executable( + 'config_test', + 'config.cpp', + '../src/config.cpp', + dependencies: test_dep, + include_directories: test_inc, +) + +test( + 'Configuration test', + config_test, + workdir: meson.source_root(), +) From 9f3b34e4d9c47eae5c3a7179e70952d5c6ba3f61 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 18:50:36 -0700 Subject: [PATCH 315/355] test: validate configuration load --- test/config.cpp | 74 ++++++++++++++++++++++++++++++++++++++ test/config/include-1.json | 6 ++++ test/config/include-2.json | 3 ++ test/config/include.json | 4 +++ test/config/multi.json | 25 +++++++++++++ test/config/simple.json | 5 +++ 6 files changed, 117 insertions(+) create mode 100644 test/config/include-1.json create mode 100644 test/config/include-2.json create mode 100644 test/config/include.json create mode 100644 test/config/multi.json create mode 100644 test/config/simple.json diff --git a/test/config.cpp b/test/config.cpp index 41180bb..78ea0e1 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -2,3 +2,77 @@ #include "config.hpp" #include + +TEST_CASE("Load simple config", "[config]") { + waybar::Config conf; + conf.load("test/config/simple.json"); + + SECTION("validate the config data") { + auto& data = conf.getConfig(); + REQUIRE(data["layer"].asString() == "top"); + REQUIRE(data["height"].asInt() == 30); + } + SECTION("select configs for configured output") { + auto configs = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); + REQUIRE(configs.size() == 1); + } + SECTION("select configs for missing output") { + auto configs = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1"); + REQUIRE(configs.empty()); + } +} + +TEST_CASE("Load config with multiple bars", "[config]") { + waybar::Config conf; + conf.load("test/config/multi.json"); + + SECTION("select multiple configs #1") { + auto data = conf.getOutputConfigs("DP-0", "Fake DisplayPort output #0"); + REQUIRE(data.size() == 3); + REQUIRE(data[0]["layer"].asString() == "bottom"); + REQUIRE(data[0]["height"].asInt() == 20); + REQUIRE(data[1]["layer"].asString() == "top"); + REQUIRE(data[1]["position"].asString() == "bottom"); + REQUIRE(data[1]["height"].asInt() == 21); + REQUIRE(data[2]["layer"].asString() == "overlay"); + REQUIRE(data[2]["position"].asString() == "right"); + REQUIRE(data[2]["height"].asInt() == 23); + } + SECTION("select multiple configs #2") { + auto data = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); + REQUIRE(data.size() == 2); + REQUIRE(data[0]["layer"].asString() == "bottom"); + REQUIRE(data[0]["height"].asInt() == 20); + REQUIRE(data[1]["layer"].asString() == "overlay"); + REQUIRE(data[1]["position"].asString() == "right"); + REQUIRE(data[1]["height"].asInt() == 23); + } + SECTION("select single config by output description") { + auto data = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1"); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["layer"].asString() == "overlay"); + REQUIRE(data[0]["position"].asString() == "left"); + REQUIRE(data[0]["height"].asInt() == 22); + } +} + +TEST_CASE("Load simple config with include", "[config]") { + waybar::Config conf; + conf.load("test/config/include.json"); + + SECTION("validate the config data") { + auto& data = conf.getConfig(); + REQUIRE(data["layer"].asString() == "bottom"); + REQUIRE(data["height"].asInt() == 30); + // config override behavior: preserve value from the top config + REQUIRE(data["position"].asString() == "top"); + } + SECTION("select configs for configured output") { + auto configs = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); + REQUIRE(configs.size() == 1); + } + SECTION("select configs for missing output") { + auto configs = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1"); + REQUIRE(configs.empty()); + } +} diff --git a/test/config/include-1.json b/test/config/include-1.json new file mode 100644 index 0000000..853111c --- /dev/null +++ b/test/config/include-1.json @@ -0,0 +1,6 @@ +{ + "layer": "top", + "position": "bottom", + "height": 30, + "output": ["HDMI-0", "DP-0"] +} diff --git a/test/config/include-2.json b/test/config/include-2.json new file mode 100644 index 0000000..741194f --- /dev/null +++ b/test/config/include-2.json @@ -0,0 +1,3 @@ +{ + "layer": "bottom" +} diff --git a/test/config/include.json b/test/config/include.json new file mode 100644 index 0000000..098cae0 --- /dev/null +++ b/test/config/include.json @@ -0,0 +1,4 @@ +{ + "include": ["test/config/include-1.json", "test/config/include-2.json"], + "position": "top" +} diff --git a/test/config/multi.json b/test/config/multi.json new file mode 100644 index 0000000..ed43a39 --- /dev/null +++ b/test/config/multi.json @@ -0,0 +1,25 @@ +[ + { + "layer": "bottom", + "height": 20, + "output": ["HDMI-0", "DP-0"] + }, + { + "position": "bottom", + "layer": "top", + "height": 21, + "output": ["DP-0"] + }, + { + "position": "left", + "layer": "overlay", + "height": 22, + "output": "Fake HDMI output #1" + }, + { + "position": "right", + "layer": "overlay", + "height": 23, + "output": "!HDMI-1" + } +] diff --git a/test/config/simple.json b/test/config/simple.json new file mode 100644 index 0000000..1cb1e3a --- /dev/null +++ b/test/config/simple.json @@ -0,0 +1,5 @@ +{ + "layer": "top", + "height": 30, + "output": ["HDMI-0", "DP-0"] +} From 8912bd3ed0734aeaf4db961e1cfab28a42ac4cb2 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 16 Aug 2021 21:11:46 +0700 Subject: [PATCH 316/355] test: multi-bar config with includes --- test/config.cpp | 34 ++++++++++++++++++++++++++++++ test/config/include-multi-0.json | 9 ++++++++ test/config/include-multi-1.json | 8 +++++++ test/config/include-multi-2.json | 9 ++++++++ test/config/include-multi-3-0.json | 8 +++++++ test/config/include-multi-3.json | 9 ++++++++ test/config/include-multi.json | 16 ++++++++++++++ 7 files changed, 93 insertions(+) create mode 100644 test/config/include-multi-0.json create mode 100644 test/config/include-multi-1.json create mode 100644 test/config/include-multi-2.json create mode 100644 test/config/include-multi-3-0.json create mode 100644 test/config/include-multi-3.json create mode 100644 test/config/include-multi.json diff --git a/test/config.cpp b/test/config.cpp index 78ea0e1..0dc0b42 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -76,3 +76,37 @@ TEST_CASE("Load simple config with include", "[config]") { REQUIRE(configs.empty()); } } + +TEST_CASE("Load multiple bar config with include", "[config]") { + waybar::Config conf; + conf.load("test/config/include-multi.json"); + + SECTION("bar config with sole include") { + auto data = conf.getOutputConfigs("OUT-0", "Fake ouptut #0"); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["height"].asInt() == 20); + } + + SECTION("bar config with output and include") { + auto data = conf.getOutputConfigs("OUT-1", "Fake output #1"); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["height"].asInt() == 21); + } + + SECTION("bar config with output override") { + auto data = conf.getOutputConfigs("OUT-2", "Fake output #2"); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["height"].asInt() == 22); + } + + SECTION("multiple levels of include") { + auto data = conf.getOutputConfigs("OUT-3", "Fake output #3"); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["height"].asInt() == 23); + } + + auto& data = conf.getConfig(); + REQUIRE(data.isArray()); + REQUIRE(data.size() == 4); + REQUIRE(data[0]["output"].asString() == "OUT-0"); +} diff --git a/test/config/include-multi-0.json b/test/config/include-multi-0.json new file mode 100644 index 0000000..87b6cab --- /dev/null +++ b/test/config/include-multi-0.json @@ -0,0 +1,9 @@ +[ + { + "output": "OUT-0", + "height": 20 + }, + {}, + {}, + {} +] diff --git a/test/config/include-multi-1.json b/test/config/include-multi-1.json new file mode 100644 index 0000000..d816a0f --- /dev/null +++ b/test/config/include-multi-1.json @@ -0,0 +1,8 @@ +[ + {}, + { + "height": 21 + }, + {}, + {} +] diff --git a/test/config/include-multi-2.json b/test/config/include-multi-2.json new file mode 100644 index 0000000..47616ef --- /dev/null +++ b/test/config/include-multi-2.json @@ -0,0 +1,9 @@ +[ + {}, + {}, + { + "output": "OUT-1", + "height": 22 + }, + {} +] diff --git a/test/config/include-multi-3-0.json b/test/config/include-multi-3-0.json new file mode 100644 index 0000000..3f4da0c --- /dev/null +++ b/test/config/include-multi-3-0.json @@ -0,0 +1,8 @@ +[ + {}, + {}, + {}, + { + "height": 23 + } +] diff --git a/test/config/include-multi-3.json b/test/config/include-multi-3.json new file mode 100644 index 0000000..d095189 --- /dev/null +++ b/test/config/include-multi-3.json @@ -0,0 +1,9 @@ +[ + {}, + {}, + {}, + { + "output": "OUT-3", + "include": "test/config/include-multi-3-0.json" + } +] diff --git a/test/config/include-multi.json b/test/config/include-multi.json new file mode 100644 index 0000000..e128aba --- /dev/null +++ b/test/config/include-multi.json @@ -0,0 +1,16 @@ +[ + { + "include": "test/config/include-multi-0.json" + }, + { + "output": "OUT-1", + "include": "test/config/include-multi-1.json" + }, + { + "output": "OUT-2", + "include": "test/config/include-multi-2.json" + }, + { + "include": "test/config/include-multi-3.json" + } +] From ccc60b42459b29fed266759bd668b697636d5253 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 14 Sep 2021 12:16:37 +0700 Subject: [PATCH 317/355] refactor(config): more sensible multi-bar include behavior --- include/config.hpp | 2 +- src/config.cpp | 20 +++++++------------- test/config.cpp | 3 ++- test/config/include-multi-0.json | 13 ++++--------- test/config/include-multi-1.json | 11 +++-------- test/config/include-multi-2.json | 13 ++++--------- test/config/include-multi-3-0.json | 11 +++-------- test/config/include-multi-3.json | 13 ++++--------- 8 files changed, 28 insertions(+), 58 deletions(-) diff --git a/include/config.hpp b/include/config.hpp index 25b78ab..82d5599 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -28,7 +28,7 @@ class Config { std::vector getOutputConfigs(const std::string &name, const std::string &identifier); private: - void setupConfig(const std::string &config_file, int depth); + void setupConfig(Json::Value &dst, const std::string &config_file, int depth); void resolveConfigIncludes(Json::Value &config, int depth); void mergeConfig(Json::Value &a_config_, Json::Value &b_config_); diff --git a/src/config.cpp b/src/config.cpp index 207e1bd..63f18c2 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -46,7 +46,7 @@ std::optional Config::findConfigPath(const std::vector return std::nullopt; } -void Config::setupConfig(const std::string &config_file, int depth) { +void Config::setupConfig(Json::Value &dst, const std::string &config_file, int depth) { if (depth > 100) { throw std::runtime_error("Aborting due to likely recursive include in config files"); } @@ -64,7 +64,7 @@ void Config::setupConfig(const std::string &config_file, int depth) { } else { resolveConfigIncludes(tmp_config, depth); } - mergeConfig(config_, tmp_config); + mergeConfig(dst, tmp_config); } void Config::resolveConfigIncludes(Json::Value &config, int depth) { @@ -72,11 +72,11 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) { if (includes.isArray()) { for (const auto &include : includes) { spdlog::info("Including resource file: {}", include.asString()); - setupConfig(tryExpandPath(include.asString()).value_or(""), ++depth); + setupConfig(config, tryExpandPath(include.asString()).value_or(""), ++depth); } } else if (includes.isString()) { spdlog::info("Including resource file: {}", includes.asString()); - setupConfig(tryExpandPath(includes.asString()).value_or(""), ++depth); + setupConfig(config, tryExpandPath(includes.asString()).value_or(""), ++depth); } } @@ -88,17 +88,11 @@ void Config::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) { for (const auto &key : b_config_.getMemberNames()) { if (a_config_[key].isObject() && b_config_[key].isObject()) { mergeConfig(a_config_[key], b_config_[key]); - } else { + } else if (a_config_[key].isNull()) { + // do not allow overriding value set by top or previously included config a_config_[key] = b_config_[key]; } } - } else if (a_config_.isArray() && b_config_.isArray()) { - // This can happen only on the top-level array of a multi-bar config - for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) { - if (a_config_[i].isObject() && b_config_[i].isObject()) { - mergeConfig(a_config_[i], b_config_[i]); - } - } } else { spdlog::error("Cannot merge config, conflicting or invalid JSON types"); } @@ -133,7 +127,7 @@ void Config::load(const std::string &config) { } config_file_ = file.value(); spdlog::info("Using configuration file {}", config_file_); - setupConfig(config_file_, 0); + setupConfig(config_, config_file_, 0); } std::vector Config::getOutputConfigs(const std::string &name, diff --git a/test/config.cpp b/test/config.cpp index 0dc0b42..343a1c1 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -62,7 +62,8 @@ TEST_CASE("Load simple config with include", "[config]") { SECTION("validate the config data") { auto& data = conf.getConfig(); - REQUIRE(data["layer"].asString() == "bottom"); + // config override behavior: preserve first included value + REQUIRE(data["layer"].asString() == "top"); REQUIRE(data["height"].asInt() == 30); // config override behavior: preserve value from the top config REQUIRE(data["position"].asString() == "top"); diff --git a/test/config/include-multi-0.json b/test/config/include-multi-0.json index 87b6cab..a4c3fc1 100644 --- a/test/config/include-multi-0.json +++ b/test/config/include-multi-0.json @@ -1,9 +1,4 @@ -[ - { - "output": "OUT-0", - "height": 20 - }, - {}, - {}, - {} -] +{ + "output": "OUT-0", + "height": 20 +} diff --git a/test/config/include-multi-1.json b/test/config/include-multi-1.json index d816a0f..2b28d6c 100644 --- a/test/config/include-multi-1.json +++ b/test/config/include-multi-1.json @@ -1,8 +1,3 @@ -[ - {}, - { - "height": 21 - }, - {}, - {} -] +{ + "height": 21 +} diff --git a/test/config/include-multi-2.json b/test/config/include-multi-2.json index 47616ef..f74c2b4 100644 --- a/test/config/include-multi-2.json +++ b/test/config/include-multi-2.json @@ -1,9 +1,4 @@ -[ - {}, - {}, - { - "output": "OUT-1", - "height": 22 - }, - {} -] +{ + "output": "OUT-1", + "height": 22 +} diff --git a/test/config/include-multi-3-0.json b/test/config/include-multi-3-0.json index 3f4da0c..11cdd3f 100644 --- a/test/config/include-multi-3-0.json +++ b/test/config/include-multi-3-0.json @@ -1,8 +1,3 @@ -[ - {}, - {}, - {}, - { - "height": 23 - } -] +{ + "height": 23 +} diff --git a/test/config/include-multi-3.json b/test/config/include-multi-3.json index d095189..309fe15 100644 --- a/test/config/include-multi-3.json +++ b/test/config/include-multi-3.json @@ -1,9 +1,4 @@ -[ - {}, - {}, - {}, - { - "output": "OUT-3", - "include": "test/config/include-multi-3-0.json" - } -] +{ + "output": "OUT-3", + "include": "test/config/include-multi-3-0.json" +} From 0c1d3e30b62dc7c94b7ae32b97089309de7ff956 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 15 Sep 2021 22:14:12 +0700 Subject: [PATCH 318/355] fix(config): preserve explicit null when merging objects --- src/config.cpp | 9 +++++++-- test/config.cpp | 2 ++ test/config/include-1.json | 3 ++- test/config/include.json | 3 ++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 63f18c2..63149cb 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1,5 +1,6 @@ #include "config.hpp" +#include #include #include #include @@ -86,11 +87,15 @@ void Config::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) { a_config_ = b_config_; } else if (a_config_.isObject() && b_config_.isObject()) { for (const auto &key : b_config_.getMemberNames()) { - if (a_config_[key].isObject() && b_config_[key].isObject()) { + // [] creates key with default value. Use `get` to avoid that. + if (a_config_.get(key, Json::Value::nullSingleton()).isObject() && + b_config_[key].isObject()) { mergeConfig(a_config_[key], b_config_[key]); - } else if (a_config_[key].isNull()) { + } else if (!a_config_.isMember(key)) { // do not allow overriding value set by top or previously included config a_config_[key] = b_config_[key]; + } else { + spdlog::trace("Option {} is already set; ignoring value {}", key, b_config_[key]); } } } else { diff --git a/test/config.cpp b/test/config.cpp index 343a1c1..f09f5da 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -67,6 +67,8 @@ TEST_CASE("Load simple config with include", "[config]") { REQUIRE(data["height"].asInt() == 30); // config override behavior: preserve value from the top config REQUIRE(data["position"].asString() == "top"); + // config override behavior: explicit null is still a value and should be preserved + REQUIRE((data.isMember("nullOption") && data["nullOption"].isNull())); } SECTION("select configs for configured output") { auto configs = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); diff --git a/test/config/include-1.json b/test/config/include-1.json index 853111c..7c47a88 100644 --- a/test/config/include-1.json +++ b/test/config/include-1.json @@ -2,5 +2,6 @@ "layer": "top", "position": "bottom", "height": 30, - "output": ["HDMI-0", "DP-0"] + "output": ["HDMI-0", "DP-0"], + "nullOption": "not null" } diff --git a/test/config/include.json b/test/config/include.json index 098cae0..c46aaf2 100644 --- a/test/config/include.json +++ b/test/config/include.json @@ -1,4 +1,5 @@ { "include": ["test/config/include-1.json", "test/config/include-2.json"], - "position": "top" + "position": "top", + "nullOption": null } From d7d606b72152da08c4a68dcbb36b92f070419159 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 15 Sep 2021 22:00:23 +0700 Subject: [PATCH 319/355] doc: update documentation for 'include' --- man/waybar.5.scd.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 9dc6925..997a48d 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -87,8 +87,9 @@ Also a minimal example configuration can be found on the at the bottom of this m *include* ++ typeof: string|array ++ - Paths to additional configuration files. In case of duplicate options, the including file's value takes precedence. Make sure to avoid circular imports. - For a multi-bar config, specify at least an empty object for each bar also in every file being included. + Paths to additional configuration files. + Each file can contain a single object with any of the bar configuration options. In case of duplicate options, the first defined value takes precedence, i.e. including file -> first included file -> etc. Nested includes are permitted, but make sure to avoid circular imports. + For a multi-bar config, the include directive affects only current bar configuration object. # MODULE FORMAT From 5991bbb741fed4756e6efdec473b56a5c9767840 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 14 Sep 2021 14:02:38 +0700 Subject: [PATCH 320/355] ci: run unit-tests --- .github/workflows/freebsd.yml | 7 ++++--- .github/workflows/linux.yml | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index f02c9b5..8af7a04 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -15,9 +15,10 @@ jobs: export CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib # sndio sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf pkg install -y git # subprojects/date - pkg install -y evdev-proto gtk-layer-shell gtkmm30 jsoncpp libdbusmenu \ - libevdev libfmt libmpdclient libudev-devd meson pkgconf pulseaudio \ - scdoc sndio spdlog + pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \ + libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ + pkgconf pulseaudio scdoc sndio spdlog run: | meson build -Dman-pages=enabled ninja -C build + meson test -C build --no-rebuild --print-errorlogs --suite waybar diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e550e20..d4efbf8 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -23,3 +23,5 @@ jobs: run: meson -Dman-pages=enabled build - name: build run: ninja -C build + - name: test + run: meson test -C build --no-rebuild --print-errorlogs --suite waybar From 4bf577e89bfc2c7c816cda99e1b5a68e63ae9d5a Mon Sep 17 00:00:00 2001 From: Darkclainer Date: Fri, 17 Sep 2021 21:18:21 +0300 Subject: [PATCH 321/355] Add CPU usage for every core --- include/modules/cpu.hpp | 11 ++++++----- src/modules/cpu/common.cpp | 32 +++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 866b5af..1fe1199 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -19,11 +20,11 @@ class Cpu : public ALabel { auto update() -> void; private: - double getCpuLoad(); - std::tuple getCpuUsage(); - std::tuple getCpuFrequency(); - std::vector> parseCpuinfo(); - std::vector parseCpuFrequencies(); + double getCpuLoad(); + std::tuple, std::string> getCpuUsage(); + std::tuple getCpuFrequency(); + std::vector> parseCpuinfo(); + std::vector parseCpuFrequencies(); std::vector> prev_times_; diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index 767cde9..5869e13 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -17,7 +17,8 @@ auto waybar::modules::Cpu::update() -> void { label_.set_tooltip_text(tooltip); } auto format = format_; - auto state = getState(cpu_usage); + auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; + auto state = getState(total_usage); if (!state.empty() && config_["format-" + state].isString()) { format = config_["format-" + state].asString(); } @@ -27,13 +28,22 @@ auto waybar::modules::Cpu::update() -> void { } else { event_box_.show(); auto icons = std::vector{state}; - label_.set_markup(fmt::format(format, - fmt::arg("load", cpu_load), - fmt::arg("usage", cpu_usage), - fmt::arg("icon", getIcon(cpu_usage, icons)), - fmt::arg("max_frequency", max_frequency), - fmt::arg("min_frequency", min_frequency), - fmt::arg("avg_frequency", avg_frequency))); + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("load", cpu_load)); + store.push_back(fmt::arg("load", cpu_load)); + store.push_back(fmt::arg("usage", total_usage)); + store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); + store.push_back(fmt::arg("max_frequency", max_frequency)); + store.push_back(fmt::arg("min_frequency", min_frequency)); + store.push_back(fmt::arg("avg_frequency", avg_frequency)); + for (size_t i = 1; i < cpu_usage.size(); ++i) { + auto core_i = i - 1; + auto core_format = fmt::format("usage{}", core_i); + store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); + auto icon_format = fmt::format("icon{}", core_i); + store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); + } + label_.set_markup(fmt::vformat(format, store)); } // Call parent update @@ -48,14 +58,14 @@ double waybar::modules::Cpu::getCpuLoad() { throw std::runtime_error("Can't get Cpu load"); } -std::tuple waybar::modules::Cpu::getCpuUsage() { +std::tuple, std::string> 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; + std::vector usage; 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]; @@ -63,11 +73,11 @@ std::tuple waybar::modules::Cpu::getCpuUsage() { 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); } + usage.push_back(tmp); } prev_times_ = curr_times; return {usage, tooltip}; From 8da940f929bd8c06a30fd3d02de53e6fac2ac5f8 Mon Sep 17 00:00:00 2001 From: Darkclainer Date: Fri, 17 Sep 2021 22:22:14 +0300 Subject: [PATCH 322/355] Update man pages for cpu usage --- man/waybar-cpu.5.scd | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 679ca2c..2e0d6c7 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -82,7 +82,9 @@ The *cpu* module displays the current cpu utilization. *{load}*: Current cpu load. -*{usage}*: Current cpu usage. +*{usage}*: Current overall cpu usage. + +*{usage*{n}*}*: Current cpu core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}. *{avg_frequency}*: Current cpu average frequency (based on all cores) in GHz. @@ -90,7 +92,13 @@ The *cpu* module displays the current cpu utilization. *{min_frequency}*: Current cpu min frequency (based on the core with the lowest frequency) in GHz. -# EXAMPLE +*{icon}*: Icon for overall cpu usage. + +*{icon*{n}*}*: Icon for cpu core n usage. Use like {icon0}. + +# EXAMPLES + +Basic configuration: ``` "cpu": { @@ -100,6 +108,16 @@ The *cpu* module displays the current cpu utilization. } ``` +Cpu usage per core rendered as icons: + +``` +"cpu": { + "interval": 1, + "format": "{icon0}{icon1}{icon2}{icon3} {usage:>2}% ", + "format-icons": ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"], +}, +``` + # STYLE - *#cpu* From 5f083193e4202e616d587b53050111fef1fa031f Mon Sep 17 00:00:00 2001 From: mazunki Date: Sat, 18 Sep 2021 01:12:58 +0200 Subject: [PATCH 323/355] fixed tab indentation to spaces, removed debug --- src/modules/network.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index a45f2b8..0fccf09 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -661,9 +661,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->clearIface(); net->ifid_ = temp_idx; net->route_priority = priority; - net->gwaddr_ = temp_gw_addr; - spdlog::debug("netwok: gateway {}", net->gwaddr_); - + net->gwaddr_ = temp_gw_addr; spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority); /* Ask ifname associated with temp_idx as well as carrier status */ From 13239417d817460eee8dd46bd09ddae0fc8dcf5a Mon Sep 17 00:00:00 2001 From: mazunki Date: Sat, 18 Sep 2021 01:20:16 +0200 Subject: [PATCH 324/355] fixed wrong dependency for make target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ccf0507..94f8ee6 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ install: build run: build ./build/waybar -debug-run: build +debug-run: build-debug ./build/waybar --log-level debug clean: From 6142dfba6a1ffc5e08ac68df172291d926fa8b03 Mon Sep 17 00:00:00 2001 From: mazunki Date: Sat, 18 Sep 2021 01:51:16 +0200 Subject: [PATCH 325/355] updated original debug message with gateway ip, similar, yet not identical to default via 10.13.37.100 dev enp7s0 metric 2 10.13.37.0/24 dev enp7s0 proto kernel scope link src 10.13.37.97 's output --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 0fccf09..3b9fb18 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -662,7 +662,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifid_ = temp_idx; net->route_priority = priority; net->gwaddr_ = temp_gw_addr; - spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority); + spdlog::debug("network: new default route via {} on if{} metric {}", temp_gw_addr, temp_idx, priority); /* Ask ifname associated with temp_idx as well as carrier status */ struct ifinfomsg ifinfo_hdr = { From 1c91c71dcde2d77506989b407eefcd18b20f34a3 Mon Sep 17 00:00:00 2001 From: mazunki Date: Sat, 18 Sep 2021 01:51:16 +0200 Subject: [PATCH 326/355] updated original debug message with gateway ip, similar, yet not identical to `ip route` --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 0fccf09..3b9fb18 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -662,7 +662,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifid_ = temp_idx; net->route_priority = priority; net->gwaddr_ = temp_gw_addr; - spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority); + spdlog::debug("network: new default route via {} on if{} metric {}", temp_gw_addr, temp_idx, priority); /* Ask ifname associated with temp_idx as well as carrier status */ struct ifinfomsg ifinfo_hdr = { From 67c730293885684a954632a70fe041abd2288685 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 18 Sep 2021 13:50:16 +0200 Subject: [PATCH 327/355] Revert "Add CPU usage for every core" --- include/modules/cpu.hpp | 11 +++++------ man/waybar-cpu.5.scd | 22 ++-------------------- src/modules/cpu/common.cpp | 32 +++++++++++--------------------- 3 files changed, 18 insertions(+), 47 deletions(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 1fe1199..866b5af 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -20,11 +19,11 @@ class Cpu : public ALabel { auto update() -> void; private: - double getCpuLoad(); - std::tuple, std::string> getCpuUsage(); - std::tuple getCpuFrequency(); - std::vector> parseCpuinfo(); - std::vector parseCpuFrequencies(); + double getCpuLoad(); + std::tuple getCpuUsage(); + std::tuple getCpuFrequency(); + std::vector> parseCpuinfo(); + std::vector parseCpuFrequencies(); std::vector> prev_times_; diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 2e0d6c7..679ca2c 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -82,9 +82,7 @@ The *cpu* module displays the current cpu utilization. *{load}*: Current cpu load. -*{usage}*: Current overall cpu usage. - -*{usage*{n}*}*: Current cpu core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}. +*{usage}*: Current cpu usage. *{avg_frequency}*: Current cpu average frequency (based on all cores) in GHz. @@ -92,13 +90,7 @@ The *cpu* module displays the current cpu utilization. *{min_frequency}*: Current cpu min frequency (based on the core with the lowest frequency) in GHz. -*{icon}*: Icon for overall cpu usage. - -*{icon*{n}*}*: Icon for cpu core n usage. Use like {icon0}. - -# EXAMPLES - -Basic configuration: +# EXAMPLE ``` "cpu": { @@ -108,16 +100,6 @@ Basic configuration: } ``` -Cpu usage per core rendered as icons: - -``` -"cpu": { - "interval": 1, - "format": "{icon0}{icon1}{icon2}{icon3} {usage:>2}% ", - "format-icons": ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"], -}, -``` - # STYLE - *#cpu* diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index 5869e13..767cde9 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -17,8 +17,7 @@ auto waybar::modules::Cpu::update() -> void { label_.set_tooltip_text(tooltip); } auto format = format_; - auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; - auto state = getState(total_usage); + auto state = getState(cpu_usage); if (!state.empty() && config_["format-" + state].isString()) { format = config_["format-" + state].asString(); } @@ -28,22 +27,13 @@ auto waybar::modules::Cpu::update() -> void { } else { event_box_.show(); auto icons = std::vector{state}; - fmt::dynamic_format_arg_store store; - store.push_back(fmt::arg("load", cpu_load)); - store.push_back(fmt::arg("load", cpu_load)); - store.push_back(fmt::arg("usage", total_usage)); - store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); - store.push_back(fmt::arg("max_frequency", max_frequency)); - store.push_back(fmt::arg("min_frequency", min_frequency)); - store.push_back(fmt::arg("avg_frequency", avg_frequency)); - for (size_t i = 1; i < cpu_usage.size(); ++i) { - auto core_i = i - 1; - auto core_format = fmt::format("usage{}", core_i); - store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); - auto icon_format = fmt::format("icon{}", core_i); - store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); - } - label_.set_markup(fmt::vformat(format, store)); + label_.set_markup(fmt::format(format, + fmt::arg("load", cpu_load), + fmt::arg("usage", cpu_usage), + fmt::arg("icon", getIcon(cpu_usage, icons)), + fmt::arg("max_frequency", max_frequency), + fmt::arg("min_frequency", min_frequency), + fmt::arg("avg_frequency", avg_frequency))); } // Call parent update @@ -58,14 +48,14 @@ double waybar::modules::Cpu::getCpuLoad() { throw std::runtime_error("Can't get Cpu load"); } -std::tuple, std::string> waybar::modules::Cpu::getCpuUsage() { +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; - std::vector usage; + 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]; @@ -73,11 +63,11 @@ std::tuple, std::string> waybar::modules::Cpu::getCpuUsage 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); } - usage.push_back(tmp); } prev_times_ = curr_times; return {usage, tooltip}; From fe547901fae1f536d047b468d8c35d3afb70a6e2 Mon Sep 17 00:00:00 2001 From: Gavin Beatty Date: Sat, 18 Sep 2021 15:28:27 -0500 Subject: [PATCH 328/355] sway/language: remove tabs, indent with 2 spaces --- src/modules/sway/language.cpp | 74 +++++++++++++++++------------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 85dc769..186fa4b 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -20,15 +20,15 @@ const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name Language::Language(const std::string& id, const Json::Value& config) : ALabel(config, "language", id, "{}", 0, true) { is_variant_displayed = format_.find("{variant}") != std::string::npos; - if (format_.find("{}") != std::string::npos || format_.find("{short}") != std::string::npos) { - displayed_short_flag |= static_cast(DispayedShortFlag::ShortName); - } - if (format_.find("{shortDescription}") != std::string::npos) { - displayed_short_flag |= static_cast(DispayedShortFlag::ShortDescription); - } - if (config.isMember("tooltip-format")) { - tooltip_format_ = config["tooltip-format"].asString(); - } + if (format_.find("{}") != std::string::npos || format_.find("{short}") != std::string::npos) { + displayed_short_flag |= static_cast(DispayedShortFlag::ShortName); + } + if (format_.find("{shortDescription}") != std::string::npos) { + displayed_short_flag |= static_cast(DispayedShortFlag::ShortDescription); + } + if (config.isMember("tooltip-format")) { + tooltip_format_ = config["tooltip-format"].asString(); + } ipc_.subscribe(R"(["input"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd)); @@ -102,16 +102,16 @@ auto Language::update() -> void { fmt::arg("variant", layout_.variant))); label_.set_markup(display_layout); if (tooltipEnabled()) { - if (tooltip_format_ != "") { - auto tooltip_display_layout = trim(fmt::format(tooltip_format_, - fmt::arg("short", layout_.short_name), - fmt::arg("shortDescription", layout_.short_description), - fmt::arg("long", layout_.full_name), - fmt::arg("variant", layout_.variant))); - label_.set_tooltip_markup(tooltip_display_layout); - } else { - label_.set_tooltip_markup(display_layout); - } + if (tooltip_format_ != "") { + auto tooltip_display_layout = trim(fmt::format(tooltip_format_, + fmt::arg("short", layout_.short_name), + fmt::arg("shortDescription", layout_.short_description), + fmt::arg("long", layout_.full_name), + fmt::arg("variant", layout_.variant))); + label_.set_tooltip_markup(tooltip_display_layout); + } else { + label_.set_tooltip_markup(display_layout); + } } event_box_.show(); @@ -126,7 +126,7 @@ auto Language::set_current_layout(std::string current_layout) -> void { auto Language::init_layouts_map(const std::vector& used_layouts) -> void { std::map> found_by_short_names; - XKBContext xkb_context; + XKBContext xkb_context; auto layout = xkb_context.next_layout(); for (; layout != nullptr; layout = xkb_context.next_layout()) { if (std::find(used_layouts.begin(), used_layouts.end(), layout->full_name) == @@ -161,15 +161,15 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> if (short_name_to_number_map.count(used_layout->short_name) == 0) { short_name_to_number_map[used_layout->short_name] = 1; } - - if (displayed_short_flag != static_cast(0)) { - int& number = short_name_to_number_map[used_layout->short_name]; - used_layout->short_name = - used_layout->short_name + std::to_string(number); - used_layout->short_description = - used_layout->short_description + std::to_string(number); - ++number; - } + + if (displayed_short_flag != static_cast(0)) { + int& number = short_name_to_number_map[used_layout->short_name]; + used_layout->short_name = + used_layout->short_name + std::to_string(number); + used_layout->short_description = + used_layout->short_description + std::to_string(number); + ++number; + } } } @@ -195,14 +195,14 @@ auto Language::XKBContext::next_layout() -> Layout* { auto variant_ = rxkb_layout_get_variant(xkb_layout_); std::string variant = variant_ == nullptr ? "" : std::string(variant_); auto short_description_ = rxkb_layout_get_brief(xkb_layout_); - std::string short_description; - if (short_description_ != nullptr) { - short_description = std::string(short_description_); - base_layouts_by_name_.emplace(name, xkb_layout_); - } else { - auto base_layout = base_layouts_by_name_[name]; - short_description = base_layout == nullptr ? "" : std::string(rxkb_layout_get_brief(base_layout)); - } + std::string short_description; + if (short_description_ != nullptr) { + short_description = std::string(short_description_); + base_layouts_by_name_.emplace(name, xkb_layout_); + } else { + auto base_layout = base_layouts_by_name_[name]; + short_description = base_layout == nullptr ? "" : std::string(rxkb_layout_get_brief(base_layout)); + } delete layout_; layout_ = new Layout{description, name, variant, short_description}; return layout_; From 6e5a0bc80a3301f4f30ce58909eadb7d54bd739e Mon Sep 17 00:00:00 2001 From: Darkclainer Date: Sun, 19 Sep 2021 13:41:59 +0300 Subject: [PATCH 329/355] Add cpu usage for every core --- include/modules/cpu.hpp | 10 +++++----- man/waybar-cpu.5.scd | 22 ++++++++++++++++++-- src/modules/cpu/common.cpp | 41 ++++++++++++++++++++++++++++---------- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 866b5af..7e32a43 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -19,11 +19,11 @@ class Cpu : public ALabel { auto update() -> void; private: - double getCpuLoad(); - std::tuple getCpuUsage(); - std::tuple getCpuFrequency(); - std::vector> parseCpuinfo(); - std::vector parseCpuFrequencies(); + double getCpuLoad(); + std::tuple, std::string> getCpuUsage(); + std::tuple getCpuFrequency(); + std::vector> parseCpuinfo(); + std::vector parseCpuFrequencies(); std::vector> prev_times_; diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 679ca2c..2e0d6c7 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -82,7 +82,9 @@ The *cpu* module displays the current cpu utilization. *{load}*: Current cpu load. -*{usage}*: Current cpu usage. +*{usage}*: Current overall cpu usage. + +*{usage*{n}*}*: Current cpu core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}. *{avg_frequency}*: Current cpu average frequency (based on all cores) in GHz. @@ -90,7 +92,13 @@ The *cpu* module displays the current cpu utilization. *{min_frequency}*: Current cpu min frequency (based on the core with the lowest frequency) in GHz. -# EXAMPLE +*{icon}*: Icon for overall cpu usage. + +*{icon*{n}*}*: Icon for cpu core n usage. Use like {icon0}. + +# EXAMPLES + +Basic configuration: ``` "cpu": { @@ -100,6 +108,16 @@ The *cpu* module displays the current cpu utilization. } ``` +Cpu usage per core rendered as icons: + +``` +"cpu": { + "interval": 1, + "format": "{icon0}{icon1}{icon2}{icon3} {usage:>2}% ", + "format-icons": ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"], +}, +``` + # STYLE - *#cpu* diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index 767cde9..4cf67eb 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -1,5 +1,14 @@ #include "modules/cpu.hpp" +// In the 80000 version of fmt library authors decided to optimize imports +// and moved declarations required for fmt::dynamic_format_arg_store in new +// header fmt/args.h +#if (FMT_VERSION >= 80000) +#include +#else +#include +#endif + waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config) : ALabel(config, "cpu", id, "{usage}%", 10) { thread_ = [this] { @@ -17,7 +26,8 @@ auto waybar::modules::Cpu::update() -> void { label_.set_tooltip_text(tooltip); } auto format = format_; - auto state = getState(cpu_usage); + auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; + auto state = getState(total_usage); if (!state.empty() && config_["format-" + state].isString()) { format = config_["format-" + state].asString(); } @@ -27,13 +37,22 @@ auto waybar::modules::Cpu::update() -> void { } else { event_box_.show(); auto icons = std::vector{state}; - label_.set_markup(fmt::format(format, - fmt::arg("load", cpu_load), - fmt::arg("usage", cpu_usage), - fmt::arg("icon", getIcon(cpu_usage, icons)), - fmt::arg("max_frequency", max_frequency), - fmt::arg("min_frequency", min_frequency), - fmt::arg("avg_frequency", avg_frequency))); + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("load", cpu_load)); + store.push_back(fmt::arg("load", cpu_load)); + store.push_back(fmt::arg("usage", total_usage)); + store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); + store.push_back(fmt::arg("max_frequency", max_frequency)); + store.push_back(fmt::arg("min_frequency", min_frequency)); + store.push_back(fmt::arg("avg_frequency", avg_frequency)); + for (size_t i = 1; i < cpu_usage.size(); ++i) { + auto core_i = i - 1; + auto core_format = fmt::format("usage{}", core_i); + store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); + auto icon_format = fmt::format("icon{}", core_i); + store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); + } + label_.set_markup(fmt::vformat(format, store)); } // Call parent update @@ -48,14 +67,14 @@ double waybar::modules::Cpu::getCpuLoad() { throw std::runtime_error("Can't get Cpu load"); } -std::tuple waybar::modules::Cpu::getCpuUsage() { +std::tuple, std::string> 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; + std::vector usage; 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]; @@ -63,11 +82,11 @@ std::tuple waybar::modules::Cpu::getCpuUsage() { 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); } + usage.push_back(tmp); } prev_times_ = curr_times; return {usage, tooltip}; From 8d04da1551bdfae54d17e16c6e49e7e0819e1f71 Mon Sep 17 00:00:00 2001 From: WuerfelDev Date: Tue, 21 Sep 2021 13:15:36 +0200 Subject: [PATCH 330/355] dont escape essid in tooltip just like https://github.com/Alexays/Waybar/pull/834 but for the network name. --- src/modules/network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 3b9fb18..14a6d86 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -342,7 +342,7 @@ auto waybar::modules::Network::update() -> void { auto text = fmt::format( format_, - fmt::arg("essid", essid_), + fmt::arg("essid", Glib::Markup::escape_text(essid_)), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("ifname", ifname_), @@ -793,7 +793,7 @@ void waybar::modules::Network::parseEssid(struct nlattr **bss) { 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); + essid_ = essid_raw; } } } From f638fe473a54e62d0ec41e143b4cf616ce388099 Mon Sep 17 00:00:00 2001 From: Ryan Walklin Date: Wed, 22 Sep 2021 11:43:25 +1200 Subject: [PATCH 331/355] Update catch2 dependency 2.13.3 -> 2.13.7 --- subprojects/catch2.wrap | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap index 356c406..c82b310 100644 --- a/subprojects/catch2.wrap +++ b/subprojects/catch2.wrap @@ -1,11 +1,11 @@ [wrap-file] -directory = Catch2-2.13.3 -source_url = https://github.com/catchorg/Catch2/archive/v2.13.3.zip -source_filename = Catch2-2.13.3.zip -source_hash = 1804feb72bc15c0856b4a43aa586c661af9c3685a75973b6a8fc0b950c7cfd13 -patch_url = https://github.com/mesonbuild/catch2/releases/download/2.13.3-2/catch2.zip -patch_filename = catch2-2.13.3-2-wrap.zip -patch_hash = 21b590ab8c65b593ad5ee8f8e5b822bf9877b2c2672f97fbb52459751053eadf +directory = Catch2-2.13.7 +source_url = https://github.com/catchorg/Catch2/archive/v2.13.7.zip +source_filename = Catch2-2.13.7.zip +source_hash = 3f3ccd90ad3a8fbb1beeb15e6db440ccdcbebe378dfd125d07a1f9a587a927e9 +patch_filename = catch2_2.13.7-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/catch2_2.13.7-1/get_patch +patch_hash = 2f7369645d747e5bd866317ac1dd4c3d04dc97d3aad4fc6b864bdf75d3b57158 [provide] catch2 = catch2_dep From fbedc3d133d627b8ff4f8c9fe5744739bf8dd0b3 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 19 Sep 2021 18:30:41 +0300 Subject: [PATCH 332/355] fix(tray): fix visibility of Passive items `show_all` call from `Tray::update` attempts to walk the widget tree and make every widget visible. Since we control individual tray item visibility based on `Status` SNI property, we don't want that to happen. Modify `Tray::update` to control the visibility of a whole tray module only and ensure that the children of `Item` are still visible when necessary. --- src/modules/sni/item.cpp | 1 + src/modules/sni/tray.cpp | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index d9748ed..b504c8d 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -62,6 +62,7 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick)); event_box.signal_scroll_event().connect(sigc::mem_fun(*this, &Item::handleScroll)); // initial visibility + event_box.show_all(); event_box.set_visible(show_passive_); cancellable_ = Gio::Cancellable::create(); diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index e73c9eb..70b0d37 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -35,11 +35,8 @@ void Tray::onRemove(std::unique_ptr& item) { } auto Tray::update() -> void { - if (box_.get_children().empty()) { - box_.hide(); - } else { - box_.show_all(); - } + // Show tray only when items are availale + box_.set_visible(!box_.get_children().empty()); // Call parent update AModule::update(); } From f18eb71ad7b6b1a523fdbb8416bbc713213b6ccd Mon Sep 17 00:00:00 2001 From: Elyes HAOUAS Date: Sat, 2 Oct 2021 18:13:17 +0200 Subject: [PATCH 333/355] Fix spelling errors Signed-off-by: Elyes HAOUAS --- resources/custom_modules/mediaplayer.py | 2 +- src/modules/clock.cpp | 2 +- src/modules/network.cpp | 2 +- src/modules/sni/tray.cpp | 2 +- test/config.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index cf3df4b..fa9aa58 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -79,7 +79,7 @@ def signal_handler(sig, frame): def parse_arguments(): parser = argparse.ArgumentParser() - # Increase verbosity with every occurence of -v + # Increase verbosity with every occurrence of -v parser.add_argument('-v', '--verbose', action='count', default=0) # Define for which player we're listening diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 82c5701..0c38cb6 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -16,7 +16,7 @@ using waybar::modules::waybar_time; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), fixed_time_zone_(false) { if (config_["timezone"].isString()) { - spdlog::warn("As using a timezone, some format args may be missing as the date library havn't got a release since 2018."); + spdlog::warn("As using a timezone, some format args may be missing as the date library haven't got a release since 2018."); time_zone_ = date::locate_zone(config_["timezone"].asString()); fixed_time_zone_ = true; } diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 3b9fb18..99ccd8e 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -469,7 +469,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } if (!is_del_event && ifi->ifi_index == net->ifid_) { - // Update inferface information + // Update interface information if (net->ifname_.empty() && ifname != NULL) { std::string new_ifname (ifname, ifname_len); net->ifname_ = new_ifname; diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index 70b0d37..c32c0d6 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -35,7 +35,7 @@ void Tray::onRemove(std::unique_ptr& item) { } auto Tray::update() -> void { - // Show tray only when items are availale + // Show tray only when items are available box_.set_visible(!box_.get_children().empty()); // Call parent update AModule::update(); diff --git a/test/config.cpp b/test/config.cpp index f09f5da..edd6d6b 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -85,7 +85,7 @@ TEST_CASE("Load multiple bar config with include", "[config]") { conf.load("test/config/include-multi.json"); SECTION("bar config with sole include") { - auto data = conf.getOutputConfigs("OUT-0", "Fake ouptut #0"); + auto data = conf.getOutputConfigs("OUT-0", "Fake output #0"); REQUIRE(data.size() == 1); REQUIRE(data[0]["height"].asInt() == 20); } From 99723845976fb577a057b86e36b502c966bf254f Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sat, 2 Oct 2021 18:35:38 +0200 Subject: [PATCH 334/355] sway/window: include floating_nodes when considering window count for class --- src/modules/sway/window.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index b677958..fc81b2c 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -68,15 +68,22 @@ auto Window::update() -> void { int leafNodesInWorkspace(const Json::Value& node) { auto const& nodes = node["nodes"]; - if(nodes.empty()) { + auto const& floating_nodes = node["floating_nodes"]; + if(nodes.empty() && floating_nodes.empty()) { if(node["type"] == "workspace") return 0; else return 1; } int sum = 0; - for(auto const& node : nodes) - sum += leafNodesInWorkspace(node); + if (!nodes.empty()) { + for(auto const& node : nodes) + sum += leafNodesInWorkspace(node); + } + if (!floating_nodes.empty()) { + for(auto const& node : floating_nodes) + sum += leafNodesInWorkspace(node); + } return sum; } From 174db444d6a6a78814e0c93430755d92312832a0 Mon Sep 17 00:00:00 2001 From: Sergey Mishin Date: Sun, 3 Oct 2021 03:27:54 +0000 Subject: [PATCH 335/355] Fix Clock crash on empty string in timezones field Also fixed timezones behavior: now waybar starting with the first timezone in timezones list and falling back to timezone field only if timezones omit or has no elements. --- include/modules/clock.hpp | 1 + man/waybar-clock.5.scd | 3 ++- src/modules/clock.cpp | 30 +++++++++++++++++++----------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index b3e2634..17752e4 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -37,6 +37,7 @@ class Clock : public ALabel { auto calendar_text(const waybar_time& wtime) -> std::string; auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void; auto first_day_of_week() -> date::weekday; + bool setTimeZone(Json::Value zone_name); }; } // namespace waybar::modules diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 28688ee..2c901d2 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -24,7 +24,8 @@ The *clock* module displays the current date and time. *timezone*: ++ typeof: string ++ default: inferred local timezone ++ - The timezone to display the time in, e.g. America/New_York. + The timezone to display the time in, e.g. America/New_York. ++ + This field will be ignored if *timezones* field is set and have at least one value. *timezones*: ++ typeof: list of strings ++ diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 0c38cb6..7c94c45 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -15,10 +15,14 @@ using waybar::modules::waybar_time; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), fixed_time_zone_(false) { - if (config_["timezone"].isString()) { + if (config_["timezones"].isArray() && !config_["timezones"].empty()) { + time_zone_idx_ = 0; + setTimeZone(config_["timezones"][time_zone_idx_]); + } else { + setTimeZone(config_["timezone"]); + } + if (fixed_time_zone_) { spdlog::warn("As using a timezone, some format args may be missing as the date library haven't got a release since 2018."); - time_zone_ = date::locate_zone(config_["timezone"].asString()); - fixed_time_zone_ = true; } if (config_["locale"].isString()) { @@ -72,6 +76,17 @@ auto waybar::modules::Clock::update() -> void { ALabel::update(); } +bool waybar::modules::Clock::setTimeZone(Json::Value zone_name) { + if (!zone_name.isString() || zone_name.asString().empty()) { + fixed_time_zone_ = false; + return false; + } + + time_zone_ = date::locate_zone(zone_name.asString()); + fixed_time_zone_ = true; + return true; +} + bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) { // defer to user commands if set if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { @@ -92,14 +107,7 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) { } else { time_zone_idx_ = time_zone_idx_ == 0 ? nr_zones - 1 : time_zone_idx_ - 1; } - auto zone_name = config_["timezones"][time_zone_idx_]; - if (!zone_name.isString() || zone_name.empty()) { - fixed_time_zone_ = false; - } else { - time_zone_ = date::locate_zone(zone_name.asString()); - fixed_time_zone_ = true; - } - + setTimeZone(config_["timezones"][time_zone_idx_]); update(); return true; } From 75a6dddea5ae5f96d40e0b823ee65bf94ef329f6 Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 20 Oct 2021 01:23:00 +0300 Subject: [PATCH 336/355] Finish --- include/modules/wlr/taskbar.hpp | 1 + man/waybar-wlr-taskbar.5.scd | 2 +- src/modules/wlr/taskbar.cpp | 20 ++++++++++++-------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 891ad55..f9233ba 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -77,6 +77,7 @@ class Task private: std::string repr() const; std::string state_string(bool = false) const; + void hide_if_ignored(); public: /* Getter functions */ diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 0e86238..6645cc2 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -70,7 +70,7 @@ Addressed by *wlr/taskbar* *ignore-list*: ++ typeof: array ++ - List of app_id to be invisible. + List of app_id/titles to be invisible. # FORMAT REPLACEMENTS diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 932a95e..b27e9f8 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -276,8 +276,7 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, struct zwlr_foreign_toplevel_handle_v1 *tl_handle, struct wl_seat *seat) : bar_{bar}, config_{config}, tbar_{tbar}, handle_{tl_handle}, seat_{seat}, id_{global_id++}, - content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, - button_visible_{false}, ignored_{false} + content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} { zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this); @@ -377,13 +376,12 @@ std::string Task::state_string(bool shortened) const void Task::handle_title(const char *title) { title_ = title; + hide_if_ignored(); } -void Task::handle_app_id(const char *app_id) +void Task::hide_if_ignored() { - app_id_ = app_id; - - if (tbar_->ignore_list().count(app_id)) { + if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) { ignored_ = true; if (button_visible_) { auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); @@ -397,6 +395,12 @@ void Task::handle_app_id(const char *app_id) handle_output_enter(output); } } +} + +void Task::handle_app_id(const char *app_id) +{ + app_id_ = app_id; + hide_if_ignored(); if (!with_icon_) return; @@ -418,13 +422,13 @@ void Task::handle_app_id(const char *app_id) void Task::handle_output_enter(struct wl_output *output) { - spdlog::debug("{} entered output {}", repr(), (void*)output); - if (ignored_) { spdlog::debug("{} is ignored", repr()); return; } + spdlog::debug("{} entered output {}", repr(), (void*)output); + if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) { /* The task entered the output of the current bar make the button visible */ tbar_->add_button(button_); From 0b66454d5c9cf526b889d16cd656cd2c4834c7ee Mon Sep 17 00:00:00 2001 From: Robin Ebert Date: Wed, 20 Oct 2021 11:11:49 +0200 Subject: [PATCH 337/355] Add spacing config option This option allows to add spaces between the modules. It uses Gtk:Box's spacing property. --- src/bar.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bar.cpp b/src/bar.cpp index 7d76359..54e487d 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -438,6 +438,13 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) center_.get_style_context()->add_class("modules-center"); right_.get_style_context()->add_class("modules-right"); + if (config["spacing"].isInt()) { + int spacing = config["spacing"].asInt(); + left_.set_spacing(spacing); + center_.set_spacing(spacing); + right_.set_spacing(spacing); + } + uint32_t height = config["height"].isUInt() ? config["height"].asUInt() : 0; uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0; From 7669029bfece53d171eead18baff0520a52859fb Mon Sep 17 00:00:00 2001 From: Robin Ebert Date: Wed, 20 Oct 2021 11:15:49 +0200 Subject: [PATCH 338/355] Add man documentation for spacing config option --- man/waybar.5.scd.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 997a48d..eaa8d78 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -64,6 +64,10 @@ Also a minimal example configuration can be found on the at the bottom of this m typeof: integer ++ Margins value without units. +*spacing* ++ + typeof: integer ++ + Size of gaps in between of the different modules. + *name* ++ typeof: string ++ Optional name added as a CSS class, for styling multiple waybars. From 01bfbc46560bf2daac3317b9541b46196e6673fd Mon Sep 17 00:00:00 2001 From: Robin Ebert Date: Wed, 20 Oct 2021 11:23:57 +0200 Subject: [PATCH 339/355] Use spacing in config --- resources/config | 1 + resources/style.css | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/config b/resources/config index b315721..063393b 100644 --- a/resources/config +++ b/resources/config @@ -3,6 +3,7 @@ // "position": "bottom", // Waybar position (top|bottom|left|right) "height": 30, // Waybar height (to be removed for auto height) // "width": 1280, // Waybar width + "spacing": 4, // Gaps between modules (4px) // Choose the order of the modules "modules-left": ["sway/workspaces", "sway/mode", "custom/media"], "modules-center": ["sway/window"], diff --git a/resources/style.css b/resources/style.css index c0d4d9b..0235942 100644 --- a/resources/style.css +++ b/resources/style.css @@ -80,7 +80,6 @@ window#waybar.chromium { #idle_inhibitor, #mpd { padding: 0 10px; - margin: 0 4px; color: #ffffff; } From a015b2e3db979fa4603f8d942d0f2e2877b9327b Mon Sep 17 00:00:00 2001 From: jonbakke <49371446+jonbakke@users.noreply.github.com> Date: Thu, 28 Oct 2021 09:37:11 -0700 Subject: [PATCH 340/355] Clarify less than/greater than in warning. I was seeing "[warning] Requested height: 20 exceeds the minimum height: 30 required..." Lines 114-134 are relevant; 133 overrides the requested height with the minimum height when GTK wants more pixels to work with. So, the code is behaving as expected, and "less than" matches the code's logic. --- src/bar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index 54e487d..a8b230e 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -13,10 +13,10 @@ namespace waybar { static constexpr const char* MIN_HEIGHT_MSG = - "Requested height: {} exceeds the minimum height: {} required by the modules"; + "Requested height: {} is less than the minimum height: {} required by the modules"; static constexpr const char* MIN_WIDTH_MSG = - "Requested width: {} exceeds the minimum width: {} required by the modules"; + "Requested width: {} is less than the minimum width: {} required by the modules"; static constexpr const char* BAR_SIZE_MSG = "Bar configured (width: {}, height: {}) for output: {}"; From decb13eef0a3c42d5bf7356c0d21f6fc5af93585 Mon Sep 17 00:00:00 2001 From: Marwin Glaser Date: Thu, 28 Oct 2021 19:10:46 +0200 Subject: [PATCH 341/355] mark zfs arc size as free in memory --- src/modules/memory/common.cpp | 4 ++-- src/modules/memory/linux.cpp | 25 ++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index 31219ed..75e0530 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -15,11 +15,11 @@ auto waybar::modules::Memory::update() -> void { unsigned long memfree; if (meminfo_.count("MemAvailable")) { // New kernels (3.4+) have an accurate available memory field. - memfree = meminfo_["MemAvailable"]; + memfree = meminfo_["MemAvailable"] + meminfo_["zfs_size"]; } else { // Old kernel; give a best-effort approximation of available memory. memfree = meminfo_["MemFree"] + meminfo_["Buffers"] + meminfo_["Cached"] + - meminfo_["SReclaimable"] - meminfo_["Shmem"]; + meminfo_["SReclaimable"] - meminfo_["Shmem"] + meminfo_["zfs_size"]; } if (memtotal > 0 && memfree >= 0) { diff --git a/src/modules/memory/linux.cpp b/src/modules/memory/linux.cpp index 75f05fe..34d55c9 100644 --- a/src/modules/memory/linux.cpp +++ b/src/modules/memory/linux.cpp @@ -1,8 +1,29 @@ #include "modules/memory.hpp" +static unsigned zfsArcSize() { + std::ifstream zfs_arc_stats{"/proc/spl/kstat/zfs/arcstats"}; + + if (zfs_arc_stats.is_open()) { + std::string name; + std::string type; + unsigned long data{0}; + + std::string line; + while (std::getline(zfs_arc_stats, line)) { + std::stringstream(line) >> name >> type >> data; + + if (name == "size") { + return data / 1024; // convert to kB + } + } + } + + return 0; +} + void waybar::modules::Memory::parseMeminfo() { const std::string data_dir_ = "/proc/meminfo"; - std::ifstream info(data_dir_); + std::ifstream info(data_dir_); if (!info.is_open()) { throw std::runtime_error("Can't open " + data_dir_); } @@ -17,4 +38,6 @@ void waybar::modules::Memory::parseMeminfo() { int64_t value = std::stol(line.substr(posDelim + 1)); meminfo_[name] = value; } + + meminfo_["zfs_size"] = zfsArcSize(); } From 48117a2e971e3d64dd1a7321802da9019a1fc9e1 Mon Sep 17 00:00:00 2001 From: Mohammad Amin Sameti Date: Fri, 29 Oct 2021 14:12:48 +0330 Subject: [PATCH 342/355] Fix divide by zero (#1303) --- src/ALabel.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index dd41a32..b9b3d1d 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -67,8 +67,10 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_ } 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 (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(); @@ -90,8 +92,10 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector& } 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 (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(); From 769b12f16a05f1c86c9966810dee93185bbc8a15 Mon Sep 17 00:00:00 2001 From: Birger Schacht <1143280+b1rger@users.noreply.github.com> Date: Sat, 6 Nov 2021 09:00:15 +0000 Subject: [PATCH 343/355] Fix typo --- man/waybar-battery.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 48c2ee1..e8053c9 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -120,7 +120,7 @@ The two arguments are: # CUSTOM FORMATS -The *battery* module allows to define custom formats based on up to two factors. The best fitting format will be selected. +The *battery* module allows one 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. From b24f9ea5690a949f91f367eb90bbe25472f55a74 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 11 Nov 2021 21:42:05 +0100 Subject: [PATCH 344/355] Ensure MPD volume is not negative If the primary output does not support changing volume MPD will report -1. Ensure that negative volume levels will be represented as 0 instead. Signed-off-by: Sefa Eyeoglu --- src/modules/mpd/mpd.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 0a7c970..6d27286 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -131,6 +131,9 @@ void waybar::modules::MPD::setLabel() { date = getTag(MPD_TAG_DATE); song_pos = mpd_status_get_song_pos(status_.get()); volume = mpd_status_get_volume(status_.get()); + if (volume < 0) { + volume = 0; + } queue_length = mpd_status_get_queue_length(status_.get()); elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get())); totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get())); From ad3f46214d791b98364dd95719ee49c9446f20a5 Mon Sep 17 00:00:00 2001 From: John Fredriksson <94405030+jfred9@users.noreply.github.com> Date: Mon, 15 Nov 2021 21:23:36 +0100 Subject: [PATCH 345/355] river/tags: Add possibility for mouse clicks Left mouse click - set-focused-tags Right mouse click - toggle-focused-tags --- include/modules/river/tags.hpp | 6 ++ man/waybar-river-tags.5.scd | 5 ++ protocol/meson.build | 1 + protocol/river-control-unstable-v1.xml | 85 ++++++++++++++++++++++++++ src/modules/river/tags.cpp | 72 +++++++++++++++++++++- 5 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 protocol/river-control-unstable-v1.xml diff --git a/include/modules/river/tags.hpp b/include/modules/river/tags.hpp index 9b75fbd..c49ec60 100644 --- a/include/modules/river/tags.hpp +++ b/include/modules/river/tags.hpp @@ -6,6 +6,7 @@ #include "AModule.hpp" #include "bar.hpp" #include "river-status-unstable-v1-client-protocol.h" +#include "river-control-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" namespace waybar::modules::river { @@ -20,7 +21,12 @@ class Tags : public waybar::AModule { void handle_view_tags(struct wl_array *tags); void handle_urgent_tags(uint32_t tags); + void handle_primary_clicked(uint32_t tag); + bool handle_button_press(GdkEventButton *event_button, uint32_t tag); + struct zriver_status_manager_v1 *status_manager_; + struct zriver_control_v1 *control_; + struct wl_seat *seat_; private: const waybar::Bar & bar_; diff --git a/man/waybar-river-tags.5.scd b/man/waybar-river-tags.5.scd index 65b9033..7814ee4 100644 --- a/man/waybar-river-tags.5.scd +++ b/man/waybar-river-tags.5.scd @@ -21,6 +21,11 @@ Addressed by *river/tags* typeof: array ++ The label to display for each tag. +*disable-click*: ++ + typeof: bool ++ + default: false ++ + If set to false, you can left click to set focused tag. Right click to toggle tag focus. If set to true this behaviour is disabled. + # EXAMPLE ``` diff --git a/protocol/meson.build b/protocol/meson.build index 07d524a..ea988ef 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -28,6 +28,7 @@ client_protocols = [ ['wlr-layer-shell-unstable-v1.xml'], ['wlr-foreign-toplevel-management-unstable-v1.xml'], ['river-status-unstable-v1.xml'], + ['river-control-unstable-v1.xml'], ] client_protos_src = [] diff --git a/protocol/river-control-unstable-v1.xml b/protocol/river-control-unstable-v1.xml new file mode 100644 index 0000000..b8faa45 --- /dev/null +++ b/protocol/river-control-unstable-v1.xml @@ -0,0 +1,85 @@ + + + + Copyright 2020 The River Developers + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + + + This interface allows clients to run compositor commands and receive a + success/failure response with output or a failure message respectively. + + Each command is built up in a series of add_argument requests and + executed with a run_command request. The first argument is the command + to be run. + + A complete list of commands should be made available in the man page of + the compositor. + + + + + This request indicates that the client will not use the + river_control object any more. Objects that have been created + through this instance are not affected. + + + + + + Arguments are stored by the server in the order they were sent until + the run_command request is made. + + + + + + + Execute the command built up using the add_argument request for the + given seat. + + + + + + + + + This object is created by the run_command request. Exactly one of the + success or failure events will be sent. This object will be destroyed + by the compositor after one of the events is sent. + + + + + Sent when the command has been successfully received and executed by + the compositor. Some commands may produce output, in which case the + output argument will be a non-empty string. + + + + + + + Sent when the command could not be carried out. This could be due to + sending a non-existent command, no command, not enough arguments, too + many arguments, invalid arguments, etc. + + + + + diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index 2628af2..87655be 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -7,7 +7,6 @@ #include "client.hpp" #include "modules/river/tags.hpp" -#include "river-status-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" namespace waybar::modules::river { @@ -33,6 +32,23 @@ static const zriver_output_status_v1_listener output_status_listener_impl{ .urgent_tags = listen_urgent_tags, }; +static void listen_command_success(void *data, + struct zriver_command_callback_v1 *zriver_command_callback_v1, + const char *output) { + // Do nothing but keep listener to avoid crashing when command was successful +} + +static void listen_command_failure(void *data, + struct zriver_command_callback_v1 *zriver_command_callback_v1, + const char *output) { + spdlog::error("failure when selecting/toggling tags {}", output); +} + +static const zriver_command_callback_v1_listener command_callback_listener_impl { + .success = listen_command_success, + .failure = listen_command_failure, +}; + static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (std::strcmp(interface, zriver_status_manager_v1_interface.name) == 0) { @@ -43,18 +59,33 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam static_cast(data)->status_manager_ = static_cast( wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, version)); } + + if (std::strcmp(interface, zriver_control_v1_interface.name) == 0) { + version = std::min(version, 1); + static_cast(data)->control_ = static_cast( + wl_registry_bind(registry, name, &zriver_control_v1_interface, version)); + } + + if (std::strcmp(interface, wl_seat_interface.name) == 0) { + version = std::min(version, 1); + static_cast(data)->seat_ = static_cast( + wl_registry_bind(registry, name, &wl_seat_interface, version)); + } } static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { /* Ignore event */ } + static const wl_registry_listener registry_listener_impl = {.global = handle_global, .global_remove = handle_global_remove}; Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &config) : waybar::AModule(config, "tags", id, false, false), status_manager_{nullptr}, + control_{nullptr}, + seat_{nullptr}, bar_(bar), box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, output_status_{nullptr} { @@ -68,6 +99,14 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con return; } + if (!control_) { + spdlog::error("river_control_v1 not advertised"); + } + + if (!seat_) { + spdlog::error("wl_seat not advertised"); + } + box_.set_name("tags"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -89,11 +128,17 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con } } + uint32_t i = 1; for (const auto &tag_label : tag_labels) { Gtk::Button &button = buttons_.emplace_back(tag_label); button.set_relief(Gtk::RELIEF_NONE); box_.pack_start(button, false, false, 0); + if (!config_["disable-click"].asBool()) { + button.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &Tags::handle_primary_clicked), i)); + button.signal_button_press_event().connect(sigc::bind(sigc::mem_fun(*this, &Tags::handle_button_press), i)); + } button.show(); + i <<= 1; } struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); @@ -107,6 +152,31 @@ Tags::~Tags() { if (output_status_) { zriver_output_status_v1_destroy(output_status_); } + + if (control_) { + zriver_control_v1_destroy(control_); + } +} + +void Tags::handle_primary_clicked(uint32_t tag) { + // Send river command to select tag on left mouse click + zriver_command_callback_v1 *callback; + zriver_control_v1_add_argument(control_, "set-focused-tags"); + zriver_control_v1_add_argument(control_, std::to_string(tag).c_str()); + callback = zriver_control_v1_run_command(control_, seat_); + zriver_command_callback_v1_add_listener(callback, &command_callback_listener_impl, nullptr); +} + +bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) { + if (event_button->type == GDK_BUTTON_PRESS && event_button->button == 3) { + // Send river command to toggle tag on right mouse click + zriver_command_callback_v1 *callback; + zriver_control_v1_add_argument(control_, "toggle-focused-tags"); + zriver_control_v1_add_argument(control_, std::to_string(tag).c_str()); + callback = zriver_control_v1_run_command(control_, seat_); + zriver_command_callback_v1_add_listener(callback, &command_callback_listener_impl, nullptr); + } + return true; } void Tags::handle_focused_tags(uint32_t tags) { From 23955fdcc25ea40cc8d7b4d1386cc83f0ed0ddff Mon Sep 17 00:00:00 2001 From: Anakael Date: Fri, 19 Nov 2021 18:38:59 +0300 Subject: [PATCH 346/355] Update taskbar.hpp --- include/modules/wlr/taskbar.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index f9233ba..cdef9f1 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -77,7 +77,7 @@ class Task private: std::string repr() const; std::string state_string(bool = false) const; - void hide_if_ignored(); + void hide_if_ignored(); public: /* Getter functions */ From 18ea53fcbcefe445f98a6f8575bcd1944d2c7710 Mon Sep 17 00:00:00 2001 From: Anakael Date: Fri, 19 Nov 2021 18:39:49 +0300 Subject: [PATCH 347/355] Update taskbar.hpp --- include/modules/wlr/taskbar.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index cdef9f1..c7e6628 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -77,7 +77,7 @@ class Task private: std::string repr() const; std::string state_string(bool = false) const; - void hide_if_ignored(); + void hide_if_ignored(); public: /* Getter functions */ From cb6af026f632d4cb56546bbce73ad114c01a9957 Mon Sep 17 00:00:00 2001 From: Anakael Date: Fri, 19 Nov 2021 18:51:28 +0300 Subject: [PATCH 348/355] Update taskbar.hpp --- include/modules/wlr/taskbar.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index c7e6628..3fe032f 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -61,8 +61,8 @@ class Task Gtk::Image icon_; Gtk::Label text_before_; Gtk::Label text_after_; - bool button_visible_; - bool ignored_; + bool button_visible_ = false; + bool ignored_ = false; bool with_icon_; std::string format_before_; From 60c1706273808937ab460367d350f217e31ebfb0 Mon Sep 17 00:00:00 2001 From: Anakael Date: Fri, 19 Nov 2021 18:54:37 +0300 Subject: [PATCH 349/355] Update taskbar.cpp --- src/modules/wlr/taskbar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index b27e9f8..af1b0da 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -376,7 +376,7 @@ std::string Task::state_string(bool shortened) const void Task::handle_title(const char *title) { title_ = title; - hide_if_ignored(); + hide_if_ignored(); } void Task::hide_if_ignored() @@ -400,7 +400,7 @@ void Task::hide_if_ignored() void Task::handle_app_id(const char *app_id) { app_id_ = app_id; - hide_if_ignored(); + hide_if_ignored(); if (!with_icon_) return; From 4154492603ddec3fc8e091cf34bcbc8081fa780a Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 19 Nov 2021 20:35:51 -0800 Subject: [PATCH 350/355] ci: update FreeBSD configuration Use latest action v0.1.5. Pin runner to macos-10.15. macos-latest will start using macos-11 images without VirtualBox in less than a month[1]. [1] https://github.com/actions/virtual-environments/issues/4060 --- .github/workflows/freebsd.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 8af7a04..03e5d70 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -4,11 +4,14 @@ on: [ push, pull_request ] jobs: clang: - runs-on: macos-latest # until https://github.com/actions/runner/issues/385 + # Run actions in a FreeBSD vm on the macos-10.15 runner + # https://github.com/actions/runner/issues/385 - for FreeBSD runner support + # https://github.com/actions/virtual-environments/issues/4060 - for lack of VirtualBox on MacOS 11 runners + runs-on: macos-10.15 steps: - uses: actions/checkout@v2 - name: Test in FreeBSD VM - uses: vmactions/freebsd-vm@v0.1.4 # aka FreeBSD 12.2 + uses: vmactions/freebsd-vm@v0.1.5 # aka FreeBSD 13.0 with: usesh: true prepare: | From ffeecf626ceaaa3dfd2ac4f2a9bd6b0c3f5a40ae Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 21 Nov 2021 15:01:25 +0300 Subject: [PATCH 351/355] Update names --- include/modules/wlr/workspace_manager.hpp | 16 +-- .../modules/wlr/workspace_manager_binding.hpp | 10 +- ...e-v1.xml => ext-workspace-unstable-v1.xml} | 104 ++++++++++++------ protocol/meson.build | 2 +- src/modules/wlr/workspace_manager.cpp | 28 ++--- src/modules/wlr/workspace_manager_binding.cpp | 54 ++++----- 6 files changed, 125 insertions(+), 89 deletions(-) rename protocol/{wlr-workspace-unstable-v1.xml => ext-workspace-unstable-v1.xml} (73%) diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index 73c5893..02f4e37 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -11,7 +11,7 @@ #include "AModule.hpp" #include "bar.hpp" -#include "wlr-workspace-unstable-v1-client-protocol.h" +#include "ext-workspace-unstable-v1-client-protocol.h" namespace waybar::modules::wlr { @@ -21,7 +21,7 @@ class WorkspaceGroup; class Workspace { public: Workspace(const waybar::Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, - zwlr_workspace_handle_v1 *workspace, uint32_t id); + zext_workspace_handle_v1 *workspace, uint32_t id); ~Workspace(); auto update() -> void; @@ -51,7 +51,7 @@ class Workspace { WorkspaceGroup & workspace_group_; // wlr stuff - zwlr_workspace_handle_v1 *workspace_handle_; + zext_workspace_handle_v1 *workspace_handle_; uint32_t state_ = 0; uint32_t id_; @@ -69,7 +69,7 @@ class Workspace { class WorkspaceGroup { public: WorkspaceGroup(const waybar::Bar &bar, Gtk::Box &box, const Json::Value &config, - WorkspaceManager &manager, zwlr_workspace_group_handle_v1 *workspace_group_handle, + WorkspaceManager &manager, zext_workspace_group_handle_v1 *workspace_group_handle, uint32_t id); ~WorkspaceGroup(); auto update() -> void; @@ -79,7 +79,7 @@ class WorkspaceGroup { auto remove_workspace(uint32_t id_) -> void; // wlr stuff - auto handle_workspace_create(zwlr_workspace_handle_v1 *workspace_handle) -> void; + auto handle_workspace_create(zext_workspace_handle_v1 *workspace_handle) -> void; auto handle_remove() -> void; auto handle_output_enter(wl_output *output) -> void; auto handle_output_leave() -> void; @@ -98,7 +98,7 @@ class WorkspaceGroup { WorkspaceManager & workspace_manager_; // wlr stuff - zwlr_workspace_group_handle_v1 *workspace_group_handle_; + zext_workspace_group_handle_v1 *workspace_group_handle_; wl_output * output_ = nullptr; uint32_t id_; @@ -117,7 +117,7 @@ class WorkspaceManager : public AModule { // wlr stuff auto register_manager(wl_registry *registry, uint32_t name, uint32_t version) -> void; - auto handle_workspace_group_create(zwlr_workspace_group_handle_v1 *workspace_group_handle) + auto handle_workspace_group_create(zext_workspace_group_handle_v1 *workspace_group_handle) -> void; auto handle_done() -> void; auto handle_finished() -> void; @@ -130,7 +130,7 @@ class WorkspaceManager : public AModule { std::vector> groups_; // wlr stuff - zwlr_workspace_manager_v1 *workspace_manager_ = nullptr; + zext_workspace_manager_v1 *workspace_manager_ = nullptr; static uint32_t group_global_id; }; diff --git a/include/modules/wlr/workspace_manager_binding.hpp b/include/modules/wlr/workspace_manager_binding.hpp index 1cef652..0bfe663 100644 --- a/include/modules/wlr/workspace_manager_binding.hpp +++ b/include/modules/wlr/workspace_manager_binding.hpp @@ -1,8 +1,8 @@ -#include "wlr-workspace-unstable-v1-client-protocol.h" +#include "ext-workspace-unstable-v1-client-protocol.h" namespace waybar::modules::wlr { void add_registry_listener(void *data); - void add_workspace_listener(zwlr_workspace_handle_v1 *workspace_handle, void *data); - void add_workspace_group_listener(zwlr_workspace_group_handle_v1 *workspace_group_handle, void *data); - zwlr_workspace_manager_v1* workspace_manager_bind(wl_registry *registry, uint32_t name, uint32_t version, void *data); -} \ No newline at end of file + void add_workspace_listener(zext_workspace_handle_v1 *workspace_handle, void *data); + void add_workspace_group_listener(zext_workspace_group_handle_v1 *workspace_group_handle, void *data); + zext_workspace_manager_v1* workspace_manager_bind(wl_registry *registry, uint32_t name, uint32_t version, void *data); +} diff --git a/protocol/wlr-workspace-unstable-v1.xml b/protocol/ext-workspace-unstable-v1.xml similarity index 73% rename from protocol/wlr-workspace-unstable-v1.xml rename to protocol/ext-workspace-unstable-v1.xml index ae0e44d..24410b6 100644 --- a/protocol/wlr-workspace-unstable-v1.xml +++ b/protocol/ext-workspace-unstable-v1.xml @@ -1,5 +1,5 @@ - + Copyright © 2019 Christopher Billington Copyright © 2020 Ilia Bozhinov @@ -26,7 +26,7 @@ THIS SOFTWARE. - + Workspaces, also called virtual desktops, are groups of surfaces. A compositor with a concept of workspaces may only show some such groups of @@ -45,7 +45,7 @@ docks by providing them with a list of workspaces and their properties, and allowing them to activate and deactivate workspaces. - After a client binds the zwlr_workspace_manager_v1, each workspace will be + After a client binds the zext_workspace_manager_v1, each workspace will be sent via the workspace event. @@ -55,9 +55,9 @@ All initial details of the workspace group (workspaces, outputs) will be sent immediately after this event via the corresponding events in - zwlr_workspace_group_handle_v1. + zext_workspace_group_handle_v1. - + @@ -68,21 +68,21 @@ This allows changes to the workspace properties to be seen as atomic, even if they happen via multiple events, and even if they involve - multiple zwlr_workspace_handle_v1 objects, for example, deactivating one + multiple zext_workspace_handle_v1 objects, for example, deactivating one workspace and activating another. - This event is sent after all changes in all workspace groups have been - sent. + This event is sent after all changes in all workspace groups have been + sent. - This allows changes to one or more zwlr_workspace_group_handle_v1 + This allows changes to one or more zext_workspace_group_handle_v1 properties to be seen as atomic, even if they happen via multiple events. In particular, an output moving from one workspace group to another sends an output_enter event and an output_leave event to the two - zwlr_workspace_group_handle_v1 objects in question. The compositor sends + zext_workspace_group_handle_v1 objects in question. The compositor sends the done event only after updating the output information in both workspace groups. @@ -91,7 +91,7 @@ This event indicates that the compositor is done sending events to the - zwlr_workspace_manager_v1. The server will destroy the object + zext_workspace_manager_v1. The server will destroy the object immediately after sending this request, so it will become invalid and the client should free any resources associated with it. @@ -108,14 +108,19 @@
- + - A zwlr_workspace_group_handle_v1 object represents a a workspace group + A zext_workspace_group_handle_v1 object represents a a workspace group that is assigned a set of outputs and contains a number of workspaces. The set of outputs assigned to the workspace group is conveyed to the client via output_enter and output_leave events, and its workspaces are conveyed with workspace events. + + For example, a compositor which has a set of workspaces for each output may + advertise a workspace group (and its workspaces) per output, whereas a compositor + where a workspace spans all outputs may advertise a single workspace group for all + outputs. @@ -140,26 +145,36 @@ All initial details of the workspace (name, coordinates, state) will be sent immediately after this event via the corresponding events in - zwlr_workspace_handle_v1. + zext_workspace_handle_v1. - + - This event means the zwlr_workspace_group_handle_v1 has been destroyed. - It is guaranteed there won't be any more events for this - zwlr_workspace_group_handle_v1. The zwlr_workspace_group_handle_v1 becomes - inert so any requests will be ignored except the destroy request. + This event means the zext_workspace_group_handle_v1 has been destroyed. + It is guaranteed there won't be any more events for this + zext_workspace_group_handle_v1. The zext_workspace_group_handle_v1 becomes + inert so any requests will be ignored except the destroy request. - The compositor must remove all workspaces belonging to a workspace group - before removing the workspace group. + The compositor must remove all workspaces belonging to a workspace group + before removing the workspace group. + + + Request that the compositor create a new workspace with the given name. + + There is no guarantee that the compositor will create a new workspace, + or that the created workspace will have the provided name. + + + + - - Destroys the zwlr_workspace_handle_v1 object. + + Destroys the zext_workspace_handle_v1 object. This request should be called either when the client does not want to use the workspace object any more or after the remove event to finalize @@ -168,9 +183,9 @@ - + - A zwlr_workspace_handle_v1 object represents a a workspace that handles a + A zext_workspace_handle_v1 object represents a a workspace that handles a group of surfaces. Each workspace has a name, conveyed to the client with the name event; a @@ -178,11 +193,16 @@ optionally a set of coordinates, conveyed to the client with the coordinates event. The client may request that the compositor activate or deactivate the workspace. + + Each workspace can belong to only a single workspace group. + Depepending on the compositor policy, there might be workspaces with + the same name in different workspace groups, but these workspaces are still + separate (e.g. one of them might be active while the other is not). - This event is emitted immediately after the zwlr_workspace_handle_v1 is + This event is emitted immediately after the zext_workspace_handle_v1 is created and whenever the name of the workspace changes. @@ -192,13 +212,13 @@ This event is used to organize workspaces into an N-dimensional grid within a workspace group, and if supported, is emitted immediately after - the zwlr_workspace_handle_v1 is created and whenever the coordinates of + the zext_workspace_handle_v1 is created and whenever the coordinates of the workspace change. Compositors may not send this event if they do not conceptually arrange workspaces in this way. If compositors simply number workspaces, without any geometric interpretation, they may send 1D coordinates, which clients should not interpret as implying any - geometry. Sending an empty array means that the compositor no longer - orders the workspace geometrically. + geometry. Sending an empty array means that the compositor no longer + orders the workspace geometrically. Coordinates have an arbitrary number of dimensions N with an uint32 position along each dimension. By convention if N > 1, the first @@ -214,7 +234,7 @@ - This event is emitted immediately after the zwlr_workspace_handle_v1 is + This event is emitted immediately after the zext_workspace_handle_v1 is created and each time the workspace state changes, either because of a compositor action or because of a request in this protocol. @@ -227,20 +247,28 @@ + + + + The workspace is not visible in its workspace group, and clients + attempting to visualize the compositor workspace state should not + display such workspaces. + + - This event means the zwlr_workspace_handle_v1 has been destroyed. It is + This event means the zext_workspace_handle_v1 has been destroyed. It is guaranteed there won't be any more events for this - zwlr_workspace_handle_v1. The zwlr_workspace_handle_v1 becomes inert so + zext_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so any requests will be ignored except the destroy request. - - Destroys the zwlr_workspace_handle_v1 object. + + Destroys the zext_workspace_handle_v1 object. This request should be called either when the client does not want to use the workspace object any more or after the remove event to finalize @@ -266,5 +294,13 @@ There is no guarantee the workspace will be actually deactivated. + + + + Request that this workspace be removed. + + There is no guarantee the workspace will be actually removed. + +
diff --git a/protocol/meson.build b/protocol/meson.build index 467260b..f81cb0d 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -27,7 +27,7 @@ client_protocols = [ [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['wlr-foreign-toplevel-management-unstable-v1.xml'], - ['wlr-workspace-unstable-v1.xml'], + ['ext-workspace-unstable-v1.xml'], ['river-status-unstable-v1.xml'], ] diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 20294f4..26556ec 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -44,7 +44,7 @@ auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, ui } auto WorkspaceManager::handle_workspace_group_create( - zwlr_workspace_group_handle_v1 *workspace_group_handle) -> void { + zext_workspace_group_handle_v1 *workspace_group_handle) -> void { auto new_id = ++group_global_id; groups_.push_back( std::make_unique(bar_, box_, config_, *this, workspace_group_handle, new_id)); @@ -52,7 +52,7 @@ auto WorkspaceManager::handle_workspace_group_create( } auto WorkspaceManager::handle_finished() -> void { - zwlr_workspace_manager_v1_destroy(workspace_manager_); + zext_workspace_manager_v1_destroy(workspace_manager_); workspace_manager_ = nullptr; } @@ -75,7 +75,7 @@ WorkspaceManager::~WorkspaceManager() { return; } - zwlr_workspace_manager_v1_destroy(workspace_manager_); + zext_workspace_manager_v1_destroy(workspace_manager_); workspace_manager_ = nullptr; } @@ -91,11 +91,11 @@ auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void { groups_.erase(it); } -auto WorkspaceManager::commit() -> void { zwlr_workspace_manager_v1_commit(workspace_manager_); } +auto WorkspaceManager::commit() -> void { zext_workspace_manager_v1_commit(workspace_manager_); } WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value &config, WorkspaceManager & manager, - zwlr_workspace_group_handle_v1 *workspace_group_handle, uint32_t id) + zext_workspace_group_handle_v1 *workspace_group_handle, uint32_t id) : bar_(bar), box_(box), config_(config), @@ -123,18 +123,18 @@ WorkspaceGroup::~WorkspaceGroup() { return; } - zwlr_workspace_group_handle_v1_destroy(workspace_group_handle_); + zext_workspace_group_handle_v1_destroy(workspace_group_handle_); workspace_group_handle_ = nullptr; } -auto WorkspaceGroup::handle_workspace_create(zwlr_workspace_handle_v1 *workspace) -> void { +auto WorkspaceGroup::handle_workspace_create(zext_workspace_handle_v1 *workspace) -> void { auto new_id = ++workspace_global_id; workspaces_.push_back(std::make_unique(bar_, config_, *this, workspace, new_id)); spdlog::debug("Workspace {} created", new_id); } auto WorkspaceGroup::handle_remove() -> void { - zwlr_workspace_group_handle_v1_destroy(workspace_group_handle_); + zext_workspace_group_handle_v1_destroy(workspace_group_handle_); workspace_group_handle_ = nullptr; workspace_manager_.remove_workspace_group(id_); } @@ -226,7 +226,7 @@ auto WorkspaceGroup::remove_button(Gtk::Button &button) -> void { } Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, - zwlr_workspace_handle_v1 *workspace, uint32_t id) + zext_workspace_handle_v1 *workspace, uint32_t id) : bar_(bar), config_(config), workspace_group_(workspace_group), @@ -267,7 +267,7 @@ Workspace::~Workspace() { return; } - zwlr_workspace_handle_v1_destroy(workspace_handle_); + zext_workspace_handle_v1_destroy(workspace_handle_); workspace_handle_ = nullptr; } @@ -280,7 +280,7 @@ auto Workspace::handle_state(const std::vector &state) -> void { state_ = 0; for (auto state_entry : state) { switch (state_entry) { - case ZWLR_WORKSPACE_HANDLE_V1_STATE_ACTIVE: + case ZEXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE: state_ |= (uint32_t)State::ACTIVE; break; } @@ -288,7 +288,7 @@ auto Workspace::handle_state(const std::vector &state) -> void { } auto Workspace::handle_remove() -> void { - zwlr_workspace_handle_v1_destroy(workspace_handle_); + zext_workspace_handle_v1_destroy(workspace_handle_); workspace_handle_ = nullptr; workspace_group_.remove_workspace(id_); } @@ -326,7 +326,7 @@ auto Workspace::get_icon() -> std::string { auto Workspace::handle_clicked() -> void { spdlog::debug("Workspace {} clicked", (void *)workspace_handle_); - zwlr_workspace_handle_v1_activate(workspace_handle_); + zext_workspace_handle_v1_activate(workspace_handle_); workspace_group_.commit(); } @@ -339,4 +339,4 @@ auto Workspace::handle_coordinates(const std::vector &coordinates) -> coordinates_ = coordinates; workspace_group_.sort_workspaces(); } -} // namespace waybar::modules::wlr \ No newline at end of file +} // namespace waybar::modules::wlr diff --git a/src/modules/wlr/workspace_manager_binding.cpp b/src/modules/wlr/workspace_manager_binding.cpp index c7d84bb..6a33973 100644 --- a/src/modules/wlr/workspace_manager_binding.cpp +++ b/src/modules/wlr/workspace_manager_binding.cpp @@ -9,7 +9,7 @@ namespace waybar::modules::wlr { static void handle_global(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - if (std::strcmp(interface, zwlr_workspace_manager_v1_interface.name) == 0) { + if (std::strcmp(interface, zext_workspace_manager_v1_interface.name) == 0) { static_cast(data)->register_manager(registry, name, version); } } @@ -31,72 +31,72 @@ void add_registry_listener(void *data) { } static void workspace_manager_handle_workspace_group( - void *data, zwlr_workspace_manager_v1 *_, zwlr_workspace_group_handle_v1 *workspace_group) { + void *data, zext_workspace_manager_v1 *_, zext_workspace_group_handle_v1 *workspace_group) { static_cast(data)->handle_workspace_group_create(workspace_group); } -static void workspace_manager_handle_done(void *data, zwlr_workspace_manager_v1 *_) { +static void workspace_manager_handle_done(void *data, zext_workspace_manager_v1 *_) { static_cast(data)->handle_done(); } -static void workspace_manager_handle_finished(void *data, zwlr_workspace_manager_v1 *_) { +static void workspace_manager_handle_finished(void *data, zext_workspace_manager_v1 *_) { static_cast(data)->handle_finished(); } -static const zwlr_workspace_manager_v1_listener workspace_manager_impl = { +static const zext_workspace_manager_v1_listener workspace_manager_impl = { .workspace_group = workspace_manager_handle_workspace_group, .done = workspace_manager_handle_done, .finished = workspace_manager_handle_finished, }; -zwlr_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name, +zext_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name, uint32_t version, void *data) { - auto *workspace_manager = static_cast( - wl_registry_bind(registry, name, &zwlr_workspace_manager_v1_interface, version)); + auto *workspace_manager = static_cast( + wl_registry_bind(registry, name, &zext_workspace_manager_v1_interface, version)); if (workspace_manager) - zwlr_workspace_manager_v1_add_listener(workspace_manager, &workspace_manager_impl, data); + zext_workspace_manager_v1_add_listener(workspace_manager, &workspace_manager_impl, data); else spdlog::error("Failed to register manager"); return workspace_manager; } -static void workspace_group_handle_output_enter(void *data, zwlr_workspace_group_handle_v1 *_, +static void workspace_group_handle_output_enter(void *data, zext_workspace_group_handle_v1 *_, wl_output *output) { static_cast(data)->handle_output_enter(output); } -static void workspace_group_handle_output_leave(void *data, zwlr_workspace_group_handle_v1 *_, +static void workspace_group_handle_output_leave(void *data, zext_workspace_group_handle_v1 *_, wl_output *output) { static_cast(data)->handle_output_leave(); } -static void workspace_group_handle_workspace(void *data, zwlr_workspace_group_handle_v1 *_, - zwlr_workspace_handle_v1 *workspace) { +static void workspace_group_handle_workspace(void *data, zext_workspace_group_handle_v1 *_, + zext_workspace_handle_v1 *workspace) { static_cast(data)->handle_workspace_create(workspace); } -static void workspace_group_handle_remove(void *data, zwlr_workspace_group_handle_v1 *_) { +static void workspace_group_handle_remove(void *data, zext_workspace_group_handle_v1 *_) { static_cast(data)->handle_remove(); } -static const zwlr_workspace_group_handle_v1_listener workspace_group_impl = { +static const zext_workspace_group_handle_v1_listener workspace_group_impl = { .output_enter = workspace_group_handle_output_enter, .output_leave = workspace_group_handle_output_leave, .workspace = workspace_group_handle_workspace, .remove = workspace_group_handle_remove}; -void add_workspace_group_listener(zwlr_workspace_group_handle_v1 *workspace_group_handle, +void add_workspace_group_listener(zext_workspace_group_handle_v1 *workspace_group_handle, void * data) { - zwlr_workspace_group_handle_v1_add_listener(workspace_group_handle, &workspace_group_impl, data); + zext_workspace_group_handle_v1_add_listener(workspace_group_handle, &workspace_group_impl, data); } -void workspace_handle_name(void *data, struct zwlr_workspace_handle_v1 *_, const char *name) { +void workspace_handle_name(void *data, struct zext_workspace_handle_v1 *_, const char *name) { static_cast(data)->handle_name(name); } -void workspace_handle_coordinates(void *data, struct zwlr_workspace_handle_v1 *_, +void workspace_handle_coordinates(void *data, struct zext_workspace_handle_v1 *_, struct wl_array *coordinates) { std::vector coords_vec; auto coords = static_cast(coordinates->data); @@ -107,16 +107,16 @@ void workspace_handle_coordinates(void *data, struct zwlr_workspace_handle_v1 *_ static_cast(data)->handle_coordinates(coords_vec); } -void workspace_handle_state(void *data, struct zwlr_workspace_handle_v1 *workspace_handle, +void workspace_handle_state(void *data, struct zext_workspace_handle_v1 *workspace_handle, struct wl_array *state) { std::vector state_vec; auto states = static_cast(state->data); for (size_t i = 0; i < state->size; ++i) { // To sync server and pending states - if (states[i] == ZWLR_WORKSPACE_HANDLE_V1_STATE_ACTIVE) { - zwlr_workspace_handle_v1_activate(workspace_handle); + if (states[i] == ZEXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE) { + zext_workspace_handle_v1_activate(workspace_handle); } else { - zwlr_workspace_handle_v1_deactivate(workspace_handle); + zext_workspace_handle_v1_deactivate(workspace_handle); } state_vec.push_back(states[i]); } @@ -124,17 +124,17 @@ void workspace_handle_state(void *data, struct zwlr_workspace_handle_v1 *workspa static_cast(data)->handle_state(state_vec); } -void workspace_handle_remove(void *data, struct zwlr_workspace_handle_v1 *_) { +void workspace_handle_remove(void *data, struct zext_workspace_handle_v1 *_) { static_cast(data)->handle_remove(); } -static const zwlr_workspace_handle_v1_listener workspace_impl = { +static const zext_workspace_handle_v1_listener workspace_impl = { .name = workspace_handle_name, .coordinates = workspace_handle_coordinates, .state = workspace_handle_state, .remove = workspace_handle_remove}; -void add_workspace_listener(zwlr_workspace_handle_v1 *workspace_handle, void *data) { - zwlr_workspace_handle_v1_add_listener(workspace_handle, &workspace_impl, data); +void add_workspace_listener(zext_workspace_handle_v1 *workspace_handle, void *data) { + zext_workspace_handle_v1_add_listener(workspace_handle, &workspace_impl, data); } } // namespace waybar::modules::wlr From ef4c6a9ba3c65235e0af0c6456a14f39a4974408 Mon Sep 17 00:00:00 2001 From: dmitry Date: Mon, 22 Nov 2021 01:12:55 +0300 Subject: [PATCH 352/355] Update to proto. Fix displaying. Rename classes. --- include/modules/wlr/workspace_manager.hpp | 15 ++- man/waybar-wlr-workspaces.5.scd | 11 ++- src/modules/wlr/workspace_manager.cpp | 99 ++++++++++++++----- src/modules/wlr/workspace_manager_binding.cpp | 6 -- 4 files changed, 98 insertions(+), 33 deletions(-) diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index 02f4e37..1fcfc22 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -27,6 +27,8 @@ class Workspace { auto id() const -> uint32_t { return id_; } auto is_active() const -> bool { return state_ & static_cast(State::ACTIVE); } + auto is_urgent() const -> bool { return state_ & static_cast(State::URGENT); } + auto is_hidden() const -> bool { return state_ & static_cast(State::HIDDEN); } // wlr stuff auto handle_name(const std::string &name) -> void; auto handle_coordinates(const std::vector &coordinates) -> void; @@ -34,14 +36,19 @@ class Workspace { auto handle_remove() -> void; auto handle_done() -> void; - auto handle_clicked() -> void; + auto handle_clicked(GdkEventButton *bt) -> bool; auto show() -> void { button_.show(); } auto hide() -> void { button_.hide(); } auto get_button_ref() -> Gtk::Button & { return button_; } auto get_name() -> std::string & { return name_; } auto get_coords() -> std::vector & { return coordinates_; } - enum class State { ACTIVE = 1 << 0 }; + enum class State + { + ACTIVE = 1 << 0, + URGENT = 1 << 1, + HIDDEN = 1 << 2, + }; private: auto get_icon() -> std::string; @@ -75,7 +82,7 @@ class WorkspaceGroup { auto update() -> void; auto id() const -> uint32_t { return id_; } - auto is_visible() const -> bool { return output_ != nullptr; } + auto is_visible() const -> bool; auto remove_workspace(uint32_t id_) -> void; // wlr stuff @@ -89,6 +96,7 @@ class WorkspaceGroup { auto handle_done() -> void; auto commit() -> void; auto sort_workspaces() -> void; + auto set_need_to_sort() -> void { need_to_sort = true; } private: static uint32_t workspace_global_id; @@ -105,6 +113,7 @@ class WorkspaceGroup { std::vector> workspaces_; bool sort_by_name = true; bool sort_by_coordinates = true; + bool need_to_sort = false; }; class WorkspaceManager : public AModule { diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-wlr-workspaces.5.scd index c1e827f..344d932 100644 --- a/man/waybar-wlr-workspaces.5.scd +++ b/man/waybar-wlr-workspaces.5.scd @@ -28,7 +28,7 @@ Addressed by *wlr/workspaces* *sort-by-coordinates*: ++ typeof: bool ++ - default: false ++ + default: true ++ Should workspaces be sorted by coordinates. Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first. If both are false - sort by id will be performed. @@ -39,6 +39,11 @@ Addressed by *wlr/workspaces* *{icon}*: Icon, as defined in *format-icons*. +# CLICK ACTIONS + +*activate*: Switch to workspace. +*close*: Close the workspace. + # ICONS Additional to workspace name matching, the following *format-icons* can be set. @@ -67,4 +72,6 @@ Additional to workspace name matching, the following *format-icons* can be set. - *#workspaces* - *#workspaces button* -- *#workspaces button.focused* +- *#workspaces button.active* +- *#workspaces button.urgent* +- *#workspaces button.hidden* diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 26556ec..1d67b04 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -143,15 +143,19 @@ auto WorkspaceGroup::handle_output_enter(wl_output *output) -> void { spdlog::debug("Output {} assigned to {} group", (void *)output, id_); output_ = output; - if (output != gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj())) { - return; + if (!is_visible()) { + return; } for (auto &workspace : workspaces_) { - workspace->show(); + add_button(workspace->get_button_ref()); } } +auto WorkspaceGroup::is_visible() const -> bool { + return output_ != nullptr && output_ == gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); +} + auto WorkspaceGroup::handle_output_leave() -> void { spdlog::debug("Output {} remove from {} group", (void *)output_, id_); output_ = nullptr; @@ -161,7 +165,7 @@ auto WorkspaceGroup::handle_output_leave() -> void { } for (auto &workspace : workspaces_) { - workspace->hide(); + remove_button(workspace->get_button_ref()); } } @@ -185,9 +189,16 @@ auto WorkspaceGroup::remove_workspace(uint32_t id) -> void { } auto WorkspaceGroup::handle_done() -> void { + need_to_sort = false; + if (!is_visible()) { + return; + } + for (auto &workspace : workspaces_) { workspace->handle_done(); } + + sort_workspaces(); } auto WorkspaceGroup::commit() -> void { workspace_manager_.commit(); } @@ -215,9 +226,7 @@ auto WorkspaceGroup::sort_workspaces() -> void { std::sort(workspaces_.begin(), workspaces_.end(), cmp); for (size_t i = 0; i < workspaces_.size(); ++i) { - for (auto &workspace : workspaces_) { - box_.reorder_child(workspace->get_button_ref(), i); - } + box_.reorder_child(workspaces_[i]->get_button_ref(), i); } } @@ -246,16 +255,23 @@ Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup & } } - button_.signal_clicked().connect(sigc::mem_fun(this, &Workspace::handle_clicked)); + /* Handle click events if configured */ + if (config_["on-click"].isString() || config_["on-click-middle"].isString() + || config_["on-click-right"].isString()) { + button_.add_events(Gdk::BUTTON_PRESS_MASK); + button_.signal_button_press_event().connect( + sigc::mem_fun(*this, &Workspace::handle_clicked), false); + } - workspace_group.add_button(button_); button_.set_relief(Gtk::RELIEF_NONE); content_.set_center_widget(label_); button_.add(content_); + if (!workspace_group.is_visible()) { return; } + workspace_group.add_button(button_); button_.show(); label_.show(); content_.show(); @@ -283,6 +299,12 @@ auto Workspace::handle_state(const std::vector &state) -> void { case ZEXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE: state_ |= (uint32_t)State::ACTIVE; break; + case ZEXT_WORKSPACE_HANDLE_V1_STATE_URGENT: + state_ |= (uint32_t)State::URGENT; + break; + case ZEXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN: + state_ |= (uint32_t)State::URGENT; + break; } } } @@ -293,21 +315,27 @@ auto Workspace::handle_remove() -> void { workspace_group_.remove_workspace(id_); } +auto add_or_remove_class(Glib::RefPtr context, bool condition, const std::string& class_name) { + if (condition) { + context->add_class(class_name); + } else { + context->remove_class(class_name); + } +} + auto Workspace::handle_done() -> void { spdlog::debug("Workspace {} changed to state {}", id_, state_); auto style_context = button_.get_style_context(); - if (is_active()) { - style_context->add_class("focused"); - } else { - style_context->remove_class("focused"); - } + add_or_remove_class(style_context, is_active(), "active"); + add_or_remove_class(style_context, is_urgent(), "urgent"); + add_or_remove_class(style_context, is_hidden(), "hidden"); } auto Workspace::get_icon() -> std::string { if (is_active()) { - auto focused_icon_it = icons_map_.find("focused"); - if (focused_icon_it != icons_map_.end()) { - return focused_icon_it->second; + auto active_icon_it = icons_map_.find("active"); + if (active_icon_it != icons_map_.end()) { + return active_icon_it->second; } } @@ -324,19 +352,46 @@ auto Workspace::get_icon() -> std::string { return name_; } -auto Workspace::handle_clicked() -> void { - spdlog::debug("Workspace {} clicked", (void *)workspace_handle_); - zext_workspace_handle_v1_activate(workspace_handle_); +auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { + std::string action; + if (config_["on-click"].isString() && bt->button == 1) { + action = config_["on-click"].asString(); + } + else if (config_["on-click-middle"].isString() && bt->button == 2) { + action = config_["on-click-middle"].asString(); + } + else if (config_["on-click-right"].isString() && bt->button == 3) { + action = config_["on-click-right"].asString(); + } + + if (action.empty()) + return true; + else if (action == "activate") { + zext_workspace_handle_v1_activate(workspace_handle_); + } + else if (action == "close") { + zext_workspace_handle_v1_remove(workspace_handle_); + } + else { + spdlog::warn("Unknown action {}", action); + } + workspace_group_.commit(); + + return true; } auto Workspace::handle_name(const std::string &name) -> void { + if (name_ != name) { + workspace_group_.set_need_to_sort(); + } name_ = name; - workspace_group_.sort_workspaces(); } auto Workspace::handle_coordinates(const std::vector &coordinates) -> void { + if (coordinates_ != coordinates) { + workspace_group_.set_need_to_sort(); + } coordinates_ = coordinates; - workspace_group_.sort_workspaces(); } } // namespace waybar::modules::wlr diff --git a/src/modules/wlr/workspace_manager_binding.cpp b/src/modules/wlr/workspace_manager_binding.cpp index 6a33973..1005d5c 100644 --- a/src/modules/wlr/workspace_manager_binding.cpp +++ b/src/modules/wlr/workspace_manager_binding.cpp @@ -112,12 +112,6 @@ void workspace_handle_state(void *data, struct zext_workspace_handle_v1 *workspa std::vector state_vec; auto states = static_cast(state->data); for (size_t i = 0; i < state->size; ++i) { - // To sync server and pending states - if (states[i] == ZEXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE) { - zext_workspace_handle_v1_activate(workspace_handle); - } else { - zext_workspace_handle_v1_deactivate(workspace_handle); - } state_vec.push_back(states[i]); } From 23991b65430c8b06e360ef289c0db6d9f3e297ec Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 23 Nov 2021 03:10:44 +0300 Subject: [PATCH 353/355] Finish --- include/modules/wlr/workspace_manager.hpp | 55 +++-- man/waybar-wlr-workspaces.5.scd | 10 + src/modules/wlr/workspace_manager.cpp | 198 ++++++++++++------ src/modules/wlr/workspace_manager_binding.cpp | 5 +- 4 files changed, 183 insertions(+), 85 deletions(-) diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index 1fcfc22..e4cdb4d 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -37,25 +38,24 @@ class Workspace { auto handle_done() -> void; auto handle_clicked(GdkEventButton *bt) -> bool; - auto show() -> void { button_.show(); } - auto hide() -> void { button_.hide(); } + auto show() -> void; + auto hide() -> void; auto get_button_ref() -> Gtk::Button & { return button_; } auto get_name() -> std::string & { return name_; } auto get_coords() -> std::vector & { return coordinates_; } - enum class State - { - ACTIVE = 1 << 0, - URGENT = 1 << 1, - HIDDEN = 1 << 2, + enum class State { + ACTIVE = (1 << 0), + URGENT = (1 << 1), + HIDDEN = (1 << 2), }; private: auto get_icon() -> std::string; - const Bar & bar_; + const Bar &bar_; const Json::Value &config_; - WorkspaceGroup & workspace_group_; + WorkspaceGroup &workspace_group_; // wlr stuff zext_workspace_handle_v1 *workspace_handle_; @@ -84,35 +84,36 @@ class WorkspaceGroup { auto id() const -> uint32_t { return id_; } auto is_visible() const -> bool; auto remove_workspace(uint32_t id_) -> void; + auto active_only() const -> bool; + auto creation_delayed() const -> bool; + auto workspaces() -> std::vector> & { return workspaces_; } + + auto sort_workspaces() -> void; + auto set_need_to_sort() -> void { need_to_sort = true; } + auto add_button(Gtk::Button &button) -> void; + auto remove_button(Gtk::Button &button) -> void; // wlr stuff auto handle_workspace_create(zext_workspace_handle_v1 *workspace_handle) -> void; auto handle_remove() -> void; auto handle_output_enter(wl_output *output) -> void; auto handle_output_leave() -> void; - - auto add_button(Gtk::Button &button) -> void; - auto remove_button(Gtk::Button &button) -> void; auto handle_done() -> void; auto commit() -> void; - auto sort_workspaces() -> void; - auto set_need_to_sort() -> void { need_to_sort = true; } private: static uint32_t workspace_global_id; const waybar::Bar &bar_; - Gtk::Box & box_; + Gtk::Box &box_; const Json::Value &config_; - WorkspaceManager & workspace_manager_; + WorkspaceManager &workspace_manager_; // wlr stuff zext_workspace_group_handle_v1 *workspace_group_handle_; - wl_output * output_ = nullptr; + wl_output *output_ = nullptr; uint32_t id_; std::vector> workspaces_; - bool sort_by_name = true; - bool sort_by_coordinates = true; bool need_to_sort = false; }; @@ -122,6 +123,13 @@ class WorkspaceManager : public AModule { ~WorkspaceManager() override; auto update() -> void override; + auto all_outputs() const -> bool { return all_outputs_; } + auto active_only() const -> bool { return active_only_; } + auto workspace_comparator() const + -> std::function &, std::unique_ptr &)>; + auto creation_delayed() const -> bool { return creation_delayed_; } + + auto sort_workspaces() -> void; auto remove_workspace_group(uint32_t id_) -> void; // wlr stuff @@ -130,11 +138,10 @@ class WorkspaceManager : public AModule { -> void; auto handle_done() -> void; auto handle_finished() -> void; - auto commit() -> void; private: - const waybar::Bar & bar_; + const waybar::Bar &bar_; Gtk::Box box_; std::vector> groups_; @@ -142,6 +149,12 @@ class WorkspaceManager : public AModule { zext_workspace_manager_v1 *workspace_manager_ = nullptr; static uint32_t group_global_id; + + bool sort_by_name_ = true; + bool sort_by_coordinates_ = true; + bool all_outputs_ = false; + bool active_only_ = false; + bool creation_delayed_ = false; }; } // namespace waybar::modules::wlr diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-wlr-workspaces.5.scd index 344d932..f0df5e9 100644 --- a/man/waybar-wlr-workspaces.5.scd +++ b/man/waybar-wlr-workspaces.5.scd @@ -33,6 +33,16 @@ Addressed by *wlr/workspaces* Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first. If both are false - sort by id will be performed. +*all-outputs*: ++ + typeof: bool ++ + default: false ++ + If set to false workspaces group will be shown only in assigned output. Otherwise all workspace groups are shown. + +*active-only*: ++ + typeof: bool ++ + default: false ++ + If set to true only active or urgent workspaces will be shown. + # FORMAT REPLACEMENTS *{name}*: Name of workspace assigned by compositor diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 1d67b04..9077dc8 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -5,7 +5,10 @@ #include #include +#include +#include +#include "gtkmm/widget.h" #include "modules/wlr/workspace_manager_binding.hpp" namespace waybar::modules::wlr { @@ -19,6 +22,27 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar : waybar::AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { + auto config_sort_by_name = config_["sort-by-name"]; + if (config_sort_by_name.isBool()) { + sort_by_name_ = config_sort_by_name.asBool(); + } + + auto config_sort_by_coordinates = config_["sort-by-coordinates"]; + if (config_sort_by_coordinates.isBool()) { + sort_by_coordinates_ = config_sort_by_coordinates.asBool(); + } + + auto config_all_outputs = config_["all-outputs"]; + if (config_all_outputs.isBool()) { + all_outputs_ = config_all_outputs.asBool(); + } + + auto config_active_only = config_["active-only"]; + if (config_active_only.isBool()) { + active_only_ = config_active_only.asBool(); + creation_delayed_ = active_only_; + } + box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -31,6 +55,54 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar } } +auto WorkspaceManager::workspace_comparator() const + -> std::function &, std::unique_ptr &)> { + return [=](std::unique_ptr &lhs, std::unique_ptr &rhs) { + auto is_name_less = lhs->get_name() < rhs->get_name(); + auto is_name_eq = lhs->get_name() == rhs->get_name(); + auto is_coords_less = lhs->get_coords() < rhs->get_coords(); + if (sort_by_name_) { + if (sort_by_coordinates_) { + return is_name_eq ? is_coords_less : is_name_less; + } else { + return is_name_less; + } + } + + if (sort_by_coordinates_) { + return is_coords_less; + } + + return lhs->id() < rhs->id(); + }; +} + +auto WorkspaceManager::sort_workspaces() -> void { + std::vector>> all_workspaces; + for (auto &group : groups_) { + auto &group_workspaces = group->workspaces(); + all_workspaces.reserve(all_workspaces.size() + + std::distance(group_workspaces.begin(), group_workspaces.end())); + if (!active_only()) { + all_workspaces.insert(all_workspaces.end(), group_workspaces.begin(), group_workspaces.end()); + continue; + } + + for (auto &workspace : group_workspaces) { + if (!workspace->is_active()) { + continue; + } + + all_workspaces.push_back(workspace); + } + } + + std::sort(all_workspaces.begin(), all_workspaces.end(), workspace_comparator()); + for (size_t i = 0; i < all_workspaces.size(); ++i) { + box_.reorder_child(all_workspaces[i].get()->get_button_ref(), i); + } +} + auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) -> void { if (workspace_manager_) { @@ -67,6 +139,9 @@ auto WorkspaceManager::update() -> void { for (auto &group : groups_) { group->update(); } + if (creation_delayed()) { + creation_delayed_ = false; + } AModule::update(); } @@ -94,7 +169,7 @@ auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void { auto WorkspaceManager::commit() -> void { zext_workspace_manager_v1_commit(workspace_manager_); } WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value &config, - WorkspaceManager & manager, + WorkspaceManager &manager, zext_workspace_group_handle_v1 *workspace_group_handle, uint32_t id) : bar_(bar), box_(box), @@ -103,15 +178,11 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value workspace_group_handle_(workspace_group_handle), id_(id) { add_workspace_group_listener(workspace_group_handle, this); - auto config_sort_by_name = config_["sort-by-name"]; - if (config_sort_by_name.isBool()) { - sort_by_name = config_sort_by_name.asBool(); - } +} - auto config_sort_by_coordinates = config_["sort-by-coordinates"]; - if (config_sort_by_coordinates.isBool()) { - sort_by_coordinates = config_sort_by_coordinates.asBool(); - } +auto WorkspaceGroup::active_only() const -> bool { return workspace_manager_.active_only(); } +auto WorkspaceGroup::creation_delayed() const -> bool { + return workspace_manager_.creation_delayed(); } auto WorkspaceGroup::add_button(Gtk::Button &button) -> void { @@ -143,8 +214,8 @@ auto WorkspaceGroup::handle_output_enter(wl_output *output) -> void { spdlog::debug("Output {} assigned to {} group", (void *)output, id_); output_ = output; - if (!is_visible()) { - return; + if (!is_visible() || workspace_manager_.creation_delayed()) { + return; } for (auto &workspace : workspaces_) { @@ -153,7 +224,9 @@ auto WorkspaceGroup::handle_output_enter(wl_output *output) -> void { } auto WorkspaceGroup::is_visible() const -> bool { - return output_ != nullptr && output_ == gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + return output_ != nullptr && + (workspace_manager_.all_outputs() || + output_ == gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj())); } auto WorkspaceGroup::handle_output_leave() -> void { @@ -171,6 +244,13 @@ auto WorkspaceGroup::handle_output_leave() -> void { auto WorkspaceGroup::update() -> void { for (auto &workspace : workspaces_) { + if (workspace_manager_.creation_delayed()) { + add_button(workspace->get_button_ref()); + if (is_visible() && (workspace->is_active() || workspace->is_urgent())) { + workspace->show(); + } + } + workspace->update(); } } @@ -198,41 +278,27 @@ auto WorkspaceGroup::handle_done() -> void { workspace->handle_done(); } - sort_workspaces(); + if (creation_delayed()) { + return; + } + + if (!workspace_manager_.all_outputs()) { + //sort_workspaces(); + } else { + workspace_manager_.sort_workspaces(); + } } auto WorkspaceGroup::commit() -> void { workspace_manager_.commit(); } auto WorkspaceGroup::sort_workspaces() -> void { - auto cmp = [=](std::unique_ptr &lhs, std::unique_ptr &rhs) { - auto is_name_less = lhs->get_name() < rhs->get_name(); - auto is_name_eq = lhs->get_name() == rhs->get_name(); - auto is_coords_less = lhs->get_coords() < rhs->get_coords(); - if (sort_by_name) { - if (sort_by_coordinates) { - return is_name_eq ? is_coords_less : is_name_less; - } - else { - return is_name_less; - } - } - - if (sort_by_coordinates) { - return is_coords_less; - } - - return lhs->id() < rhs->id(); - }; - - std::sort(workspaces_.begin(), workspaces_.end(), cmp); + std::sort(workspaces_.begin(), workspaces_.end(), workspace_manager_.workspace_comparator()); for (size_t i = 0; i < workspaces_.size(); ++i) { box_.reorder_child(workspaces_[i]->get_button_ref(), i); } } -auto WorkspaceGroup::remove_button(Gtk::Button &button) -> void { - box_.remove(button); -} +auto WorkspaceGroup::remove_button(Gtk::Button &button) -> void { box_.remove(button); } Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, zext_workspace_handle_v1 *workspace, uint32_t id) @@ -256,11 +322,11 @@ Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup & } /* Handle click events if configured */ - if (config_["on-click"].isString() || config_["on-click-middle"].isString() - || config_["on-click-right"].isString()) { + if (config_["on-click"].isString() || config_["on-click-middle"].isString() || + config_["on-click-right"].isString()) { button_.add_events(Gdk::BUTTON_PRESS_MASK); - button_.signal_button_press_event().connect( - sigc::mem_fun(*this, &Workspace::handle_clicked), false); + button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked), + false); } button_.set_relief(Gtk::RELIEF_NONE); @@ -272,9 +338,7 @@ Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup & } workspace_group.add_button(button_); - button_.show(); - label_.show(); - content_.show(); + button_.show_all(); } Workspace::~Workspace() { @@ -303,7 +367,7 @@ auto Workspace::handle_state(const std::vector &state) -> void { state_ |= (uint32_t)State::URGENT; break; case ZEXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN: - state_ |= (uint32_t)State::URGENT; + state_ |= (uint32_t)State::HIDDEN; break; } } @@ -315,7 +379,8 @@ auto Workspace::handle_remove() -> void { workspace_group_.remove_workspace(id_); } -auto add_or_remove_class(Glib::RefPtr context, bool condition, const std::string& class_name) { +auto add_or_remove_class(Glib::RefPtr context, bool condition, + const std::string &class_name) { if (condition) { context->add_class(class_name); } else { @@ -329,6 +394,16 @@ auto Workspace::handle_done() -> void { add_or_remove_class(style_context, is_active(), "active"); add_or_remove_class(style_context, is_urgent(), "urgent"); add_or_remove_class(style_context, is_hidden(), "hidden"); + + if (workspace_group_.creation_delayed()) { + return; + } + + if (workspace_group_.active_only() && (is_active() || is_urgent())) { + button_.show_all(); + } else if (workspace_group_.active_only() && !(is_active() || is_urgent())) { + button_.hide(); + } } auto Workspace::get_icon() -> std::string { @@ -355,24 +430,20 @@ auto Workspace::get_icon() -> std::string { auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { std::string action; if (config_["on-click"].isString() && bt->button == 1) { - action = config_["on-click"].asString(); - } - else if (config_["on-click-middle"].isString() && bt->button == 2) { - action = config_["on-click-middle"].asString(); - } - else if (config_["on-click-right"].isString() && bt->button == 3) { - action = config_["on-click-right"].asString(); + action = config_["on-click"].asString(); + } else if (config_["on-click-middle"].isString() && bt->button == 2) { + action = config_["on-click-middle"].asString(); + } else if (config_["on-click-right"].isString() && bt->button == 3) { + action = config_["on-click-right"].asString(); } if (action.empty()) - return true; + return true; else if (action == "activate") { - zext_workspace_handle_v1_activate(workspace_handle_); - } - else if (action == "close") { - zext_workspace_handle_v1_remove(workspace_handle_); - } - else { + zext_workspace_handle_v1_activate(workspace_handle_); + } else if (action == "close") { + zext_workspace_handle_v1_remove(workspace_handle_); + } else { spdlog::warn("Unknown action {}", action); } @@ -381,16 +452,19 @@ auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { return true; } +auto Workspace::show() -> void { button_.show_all(); } +auto Workspace::hide() -> void { button_.hide(); } + auto Workspace::handle_name(const std::string &name) -> void { if (name_ != name) { - workspace_group_.set_need_to_sort(); + workspace_group_.set_need_to_sort(); } name_ = name; } auto Workspace::handle_coordinates(const std::vector &coordinates) -> void { if (coordinates_ != coordinates) { - workspace_group_.set_need_to_sort(); + workspace_group_.set_need_to_sort(); } coordinates_ = coordinates; } diff --git a/src/modules/wlr/workspace_manager_binding.cpp b/src/modules/wlr/workspace_manager_binding.cpp index 1005d5c..30c6041 100644 --- a/src/modules/wlr/workspace_manager_binding.cpp +++ b/src/modules/wlr/workspace_manager_binding.cpp @@ -1,6 +1,7 @@ #include "modules/wlr/workspace_manager_binding.hpp" #include +#include #include "client.hpp" #include "modules/wlr/workspace_manager.hpp" @@ -100,7 +101,7 @@ void workspace_handle_coordinates(void *data, struct zext_workspace_handle_v1 *_ struct wl_array *coordinates) { std::vector coords_vec; auto coords = static_cast(coordinates->data); - for (size_t i = 0; i < coordinates->size; ++i) { + for (size_t i = 0; i < coordinates->size / sizeof(uint32_t); ++i) { coords_vec.push_back(coords[i]); } @@ -111,7 +112,7 @@ void workspace_handle_state(void *data, struct zext_workspace_handle_v1 *workspa struct wl_array *state) { std::vector state_vec; auto states = static_cast(state->data); - for (size_t i = 0; i < state->size; ++i) { + for (size_t i = 0; i < state->size / sizeof(uint32_t); ++i) { state_vec.push_back(states[i]); } From 98f7a10a51d6cecc1422edfcb0bc3886e0a1121a Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 23 Nov 2021 03:18:00 +0300 Subject: [PATCH 354/355] Fix sort --- src/modules/wlr/workspace_manager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 9077dc8..bacef35 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -141,6 +141,7 @@ auto WorkspaceManager::update() -> void { } if (creation_delayed()) { creation_delayed_ = false; + sort_workspaces(); } AModule::update(); } @@ -283,7 +284,7 @@ auto WorkspaceGroup::handle_done() -> void { } if (!workspace_manager_.all_outputs()) { - //sort_workspaces(); + sort_workspaces(); } else { workspace_manager_.sort_workspaces(); } From c51a973d602fb448aa0e11e37ddd7f3df0d588ee Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 Nov 2021 10:10:14 +0100 Subject: [PATCH 355/355] Revert "dont escape essid in tooltip" --- src/modules/network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 020478a..e7b20ab 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -328,7 +328,7 @@ auto waybar::modules::Network::update() -> void { auto text = fmt::format( format_, - fmt::arg("essid", Glib::Markup::escape_text(essid_)), + fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("ifname", ifname_), @@ -779,7 +779,7 @@ void waybar::modules::Network::parseEssid(struct nlattr **bss) { auto essid_end = essid_begin + ies[1]; std::string essid_raw; std::copy(essid_begin, essid_end, std::back_inserter(essid_raw)); - essid_ = essid_raw; + essid_ = Glib::Markup::escape_text(essid_raw); } } }