From 94a4d41a65c98b6c95e2f520a57911321ae256ec Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 26 May 2020 07:29:54 +0000 Subject: [PATCH 01/66] disk: make used calculation match df(1) output Substract space only available to superuser as well. --- src/modules/disk.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index 59ffea6..658fcd9 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -45,7 +45,7 @@ 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 used = pow_format((stats.f_blocks - stats.f_bfree) * stats.f_frsize, "B", true); auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true); label_.set_markup(fmt::format(format_ @@ -53,7 +53,7 @@ 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", (stats.f_blocks - stats.f_bfree) * 100 / stats.f_blocks) , fmt::arg("total", total) , fmt::arg("path", path_) )); @@ -67,7 +67,7 @@ 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", (stats.f_blocks - stats.f_bfree) * 100 / stats.f_blocks) , fmt::arg("total", total) , fmt::arg("path", path_) )); From 2b11b7ef8c9b66e9f00ec573fa3de3f14e724e0d Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 5 Aug 2020 02:17:38 +0300 Subject: [PATCH 02/66] 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 edd4d8ee119f1ebeb51ecbac814784e8980e08ca Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 5 Aug 2020 23:10:08 +0300 Subject: [PATCH 03/66] 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 7638f5c5958d0f9243c8b71569b9cb048f439a7a Mon Sep 17 00:00:00 2001 From: dmitry Date: Thu, 6 Aug 2020 01:42:57 +0300 Subject: [PATCH 04/66] 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 4c251578e923760372bf51f2d9540d7abd9b204b Mon Sep 17 00:00:00 2001 From: dmitry Date: Fri, 7 Aug 2020 02:45:08 +0300 Subject: [PATCH 05/66] 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 06/66] 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 07/66] 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 22409d27c5a6307880828841a8b7c4a113b3dc1b Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 11 Aug 2020 00:28:56 +0300 Subject: [PATCH 08/66] 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 285a264aae64780f9c8fbf22f88ebdf7ee55ab86 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 28 Dec 2020 17:26:55 -0800 Subject: [PATCH 09/66] feat(util): SafeSignal class for cross-thread signals with arguments Implement a wrapper over Glib::Dispatcher that passes the arguments to the signal consumer via synchronized `std::queue`. Arguments are always passed by value and the return type of the signal is expected to be `void`. --- include/util/SafeSignal.hpp | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 include/util/SafeSignal.hpp diff --git a/include/util/SafeSignal.hpp b/include/util/SafeSignal.hpp new file mode 100644 index 0000000..b2beff4 --- /dev/null +++ b/include/util/SafeSignal.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace waybar { + +/** + * Thread-safe signal wrapper. + * Uses Glib::Dispatcher to pass events to another thread and locked queue to pass the arguments. + */ +template +struct SafeSignal : sigc::signal...)> { + public: + SafeSignal() { dp_.connect(sigc::mem_fun(*this, &SafeSignal::handle_event)); } + + template + void emit(EmitArgs&&... args) { + { + std::unique_lock lock(mutex_); + queue_.emplace(std::forward(args)...); + } + dp_.emit(); + } + + template + inline void operator()(EmitArgs&&... args) { + emit(std::forward(args)...); + } + + protected: + using signal_t = sigc::signal...)>; + using arg_tuple_t = std::tuple...>; + // ensure that unwrapped methods are not accessible + using signal_t::emit_reverse; + using signal_t::make_slot; + + void handle_event() { + auto fn = signal_t::make_slot(); + for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) { + auto args = queue_.front(); + queue_.pop(); + lock.unlock(); + std::apply(fn, args); + } + } + + Glib::Dispatcher dp_; + std::mutex mutex_; + std::queue queue_; +}; + +} // namespace waybar From 8a0e76c8d8e433d78ee5cfe19bec428623fa9a51 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Oct 2020 18:42:25 -0700 Subject: [PATCH 10/66] fix(util): avoid creating temporary functor for each event --- include/util/SafeSignal.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/util/SafeSignal.hpp b/include/util/SafeSignal.hpp index b2beff4..15a8d2b 100644 --- a/include/util/SafeSignal.hpp +++ b/include/util/SafeSignal.hpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace waybar { @@ -36,24 +37,26 @@ struct SafeSignal : sigc::signal...)> { protected: using signal_t = sigc::signal...)>; + using slot_t = decltype(std::declval().make_slot()); using arg_tuple_t = std::tuple...>; // ensure that unwrapped methods are not accessible using signal_t::emit_reverse; using signal_t::make_slot; void handle_event() { - auto fn = signal_t::make_slot(); for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) { auto args = queue_.front(); queue_.pop(); lock.unlock(); - std::apply(fn, args); + std::apply(cached_fn_, args); } } Glib::Dispatcher dp_; std::mutex mutex_; std::queue queue_; + // cache functor for signal emission to avoid recreating it on each event + const slot_t cached_fn_ = make_slot(); }; } // namespace waybar From 79883dbce4caf5d8e66ff70af1d59a3a1950fc57 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 28 Dec 2020 17:31:23 -0800 Subject: [PATCH 11/66] feat(util): optimize SafeSignal for events from the main thread --- include/util/SafeSignal.hpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/include/util/SafeSignal.hpp b/include/util/SafeSignal.hpp index 15a8d2b..3b68653 100644 --- a/include/util/SafeSignal.hpp +++ b/include/util/SafeSignal.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -23,11 +24,22 @@ struct SafeSignal : sigc::signal...)> { template void emit(EmitArgs&&... args) { - { - std::unique_lock lock(mutex_); - queue_.emplace(std::forward(args)...); + if (main_tid_ == std::this_thread::get_id()) { + /* + * Bypass the queue if the method is called the main thread. + * Ensures that events emitted from the main thread are processed synchronously and saves a + * few CPU cycles on locking/queuing. + * As a downside, this makes main thread events prioritized over the other threads and + * disrupts chronological order. + */ + signal_t::emit(std::forward(args)...); + } else { + { + std::unique_lock lock(mutex_); + queue_.emplace(std::forward(args)...); + } + dp_.emit(); } - dp_.emit(); } template @@ -55,6 +67,7 @@ struct SafeSignal : sigc::signal...)> { Glib::Dispatcher dp_; std::mutex mutex_; std::queue queue_; + const std::thread::id main_tid_ = std::this_thread::get_id(); // cache functor for signal emission to avoid recreating it on each event const slot_t cached_fn_ = make_slot(); }; From 3e2197a82a9a0f790d484c89bd1655229f97f370 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 28 Dec 2020 17:28:03 -0800 Subject: [PATCH 12/66] test(util): add tests for SafeSignal Add a fixture for writing tests that require interaction with Glib event loop and a very basic test for SafeSignal. --- test/GlibTestsFixture.hpp | 19 ++++++++++++ test/SafeSignal.cpp | 63 +++++++++++++++++++++++++++++++++++++++ test/meson.build | 14 +++++++++ 3 files changed, 96 insertions(+) create mode 100644 test/GlibTestsFixture.hpp create mode 100644 test/SafeSignal.cpp diff --git a/test/GlibTestsFixture.hpp b/test/GlibTestsFixture.hpp new file mode 100644 index 0000000..ffe6fd3 --- /dev/null +++ b/test/GlibTestsFixture.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +/** + * Minimal Glib application to be used for tests that require Glib main loop + */ +class GlibTestsFixture : public sigc::trackable { + public: + GlibTestsFixture() : main_loop_{Glib::MainLoop::create()} {} + + void run(std::function fn) { + Glib::signal_idle().connect_once(fn); + main_loop_->run(); + } + + void quit() { main_loop_->quit(); } + + protected: + Glib::RefPtr main_loop_; +}; diff --git a/test/SafeSignal.cpp b/test/SafeSignal.cpp new file mode 100644 index 0000000..b07e9ca --- /dev/null +++ b/test/SafeSignal.cpp @@ -0,0 +1,63 @@ +#define CATCH_CONFIG_RUNNER +#include "util/SafeSignal.hpp" + +#include + +#include +#include + +#include "GlibTestsFixture.hpp" + +using namespace waybar; +/** + * Basic sanity test for SafeSignal: + * check that type deduction works, events are delivered and the order is right + * Running this with -fsanitize=thread should not fail + */ +TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal basic functionality", "[signal][thread][util]") { + const int NUM_EVENTS = 100; + int count = 0; + int last_value = 0; + + SafeSignal test_signal; + + const auto main_tid = std::this_thread::get_id(); + std::thread producer; + + // timeout the test in 500ms + Glib::signal_timeout().connect_once([]() { throw std::runtime_error("Test timed out"); }, 500); + + test_signal.connect([&](auto val, auto str) { + static_assert(std::is_same::value); + static_assert(std::is_same::value); + // check that we're in the same thread as the main loop + REQUIRE(std::this_thread::get_id() == main_tid); + // check event order + REQUIRE(val == last_value + 1); + + last_value = val; + if (++count >= NUM_EVENTS) { + this->quit(); + }; + }); + + run([&]() { + // check that events from the same thread are delivered and processed synchronously + test_signal.emit(1, "test"); + REQUIRE(count == 1); + + // start another thread and generate events + producer = std::thread([&]() { + for (auto i = 2; i <= NUM_EVENTS; ++i) { + test_signal.emit(i, "test"); + } + }); + }); + producer.join(); + REQUIRE(count == NUM_EVENTS); +} + +int main(int argc, char* argv[]) { + Glib::init(); + return Catch::Session().run(argc, argv); +} diff --git a/test/meson.build b/test/meson.build index 85b9771..bbef21e 100644 --- a/test/meson.build +++ b/test/meson.build @@ -2,6 +2,7 @@ test_inc = include_directories('../include') test_dep = [ catch2, fmt, + gtkmm, jsoncpp, spdlog, ] @@ -14,8 +15,21 @@ config_test = executable( include_directories: test_inc, ) +safesignal_test = executable( + 'safesignal_test', + 'SafeSignal.cpp', + dependencies: test_dep, + include_directories: test_inc, +) + test( 'Configuration test', config_test, workdir: meson.source_root(), ) + +test( + 'SafeSignal test', + safesignal_test, + workdir: meson.source_root(), +) From 03a641ed8343c825635732e9d4c0694160c380e6 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 15 Sep 2021 22:35:50 +0700 Subject: [PATCH 13/66] feat(bar): support swaybar `mode` for configuring window Use `mode` (`waybar::Bar::setMode`) as a shorthand to configure bar visibility, layer, exclusive zones and input event handling in the same way as `swaybar` does. See `sway-bar(5)` for a description of available modes. --- include/bar.hpp | 1 + src/bar.cpp | 77 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 6f3dfcf..1f90f9a 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -58,6 +58,7 @@ class Bar { Bar(const Bar &) = delete; ~Bar() = default; + void setMode(const std::string &); void setVisible(bool visible); void toggle(); void handleSignal(int); diff --git a/src/bar.cpp b/src/bar.cpp index a8b230e..3ce59e3 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -403,27 +403,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) window.get_style_context()->add_class(config["name"].asString()); window.get_style_context()->add_class(config["position"].asString()); - if (config["layer"] == "top") { - layer_ = bar_layer::TOP; - } else if (config["layer"] == "overlay") { - layer_ = bar_layer::OVERLAY; - } - - if (config["exclusive"].isBool()) { - exclusive = config["exclusive"].asBool(); - } else if (layer_ == bar_layer::OVERLAY) { - // swaybar defaults: overlay mode does not reserve an exclusive zone - exclusive = false; - } - - bool passthrough = false; - if (config["passthrough"].isBool()) { - passthrough = config["passthrough"].asBool(); - } else if (layer_ == bar_layer::OVERLAY) { - // swaybar defaults: overlay mode does not accept pointer events. - passthrough = true; - } - auto position = config["position"].asString(); if (position == "right" || position == "left") { @@ -505,13 +484,39 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) surface_impl_ = std::make_unique(window, *output); } - surface_impl_->setLayer(layer_); - surface_impl_->setExclusiveZone(exclusive); surface_impl_->setMargins(margins_); - surface_impl_->setPassThrough(passthrough); surface_impl_->setPosition(position); surface_impl_->setSize(width, height); + if (auto mode = config["mode"]; mode.isString()) { + setMode(mode.asString()); + } else { + if (config["layer"] == "top") { + layer_ = bar_layer::TOP; + } else if (config["layer"] == "overlay") { + layer_ = bar_layer::OVERLAY; + } + + if (config["exclusive"].isBool()) { + exclusive = config["exclusive"].asBool(); + } else if (layer_ == bar_layer::OVERLAY) { + // swaybar defaults: overlay mode does not reserve an exclusive zone + exclusive = false; + } + + bool passthrough = false; + if (config["passthrough"].isBool()) { + passthrough = config["passthrough"].asBool(); + } else if (layer_ == bar_layer::OVERLAY) { + // swaybar defaults: overlay mode does not accept pointer events. + passthrough = true; + } + + surface_impl_->setLayer(layer_); + surface_impl_->setExclusiveZone(exclusive); + surface_impl_->setPassThrough(passthrough); + } + window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap)); setupWidgets(); @@ -528,6 +533,30 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) } } +void waybar::Bar::setMode(const std::string& mode) { + bool passthrough = false; + visible = true; + exclusive = true; + layer_ = bar_layer::BOTTOM; + + if (mode == "hide") { + exclusive = false; + layer_ = bar_layer::TOP; + visible = false; + } else if (mode == "invisible") { + visible = false; + } else if (mode == "overlay") { + exclusive = false; + layer_ = bar_layer::TOP; + passthrough = true; + } + + surface_impl_->setLayer(layer_); + surface_impl_->setExclusiveZone(exclusive); + surface_impl_->setPassThrough(passthrough); + setVisible(visible); +} + void waybar::Bar::onMap(GdkEventAny*) { /* * Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor). From 6d2ba7a75b2399d38c8ee8719f172ec09ec298fc Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 19 Nov 2021 19:29:51 -0800 Subject: [PATCH 14/66] feat(bar): store modes as a map of presets This allows to apply the mode atomically and adds possibility of defining custom modes (to be implemented). --- include/bar.hpp | 21 +++++++++++++++-- src/bar.cpp | 62 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 1f90f9a..426c55c 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -36,6 +36,13 @@ struct bar_margins { int left = 0; }; +struct bar_mode { + bar_layer layer; + bool exclusive; + bool passthrough; + bool visible; +}; + class BarSurface { protected: BarSurface() = default; @@ -54,18 +61,23 @@ class BarSurface { class Bar { public: + using bar_mode_map = std::map; + static const bar_mode_map PRESET_MODES; + static const std::string_view MODE_DEFAULT; + static const std::string_view MODE_INVISIBLE; + Bar(struct waybar_output *w_output, const Json::Value &); Bar(const Bar &) = delete; ~Bar() = default; - void setMode(const std::string &); + void setMode(const std::string_view &); void setVisible(bool visible); void toggle(); void handleSignal(int); struct waybar_output *output; Json::Value config; - struct wl_surface * surface; + struct wl_surface *surface; bool exclusive = true; bool visible = true; bool vertical = false; @@ -77,6 +89,11 @@ class Bar { void getModules(const Factory &, const std::string &); void setupAltFormatKeyForModule(const std::string &module_name); void setupAltFormatKeyForModuleList(const char *module_list_name); + void setMode(const bar_mode &); + + /* Copy initial set of modes to allow customization */ + bar_mode_map configured_modes = PRESET_MODES; + std::string last_mode_{MODE_DEFAULT}; std::unique_ptr surface_impl_; bar_layer layer_; diff --git a/src/bar.cpp b/src/bar.cpp index 3ce59e3..2f8bc27 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -23,6 +23,35 @@ static constexpr const char* BAR_SIZE_MSG = "Bar configured (width: {}, height: static constexpr const char* SIZE_DEFINED = "{} size is defined in the config file so it will stay like that"; +const Bar::bar_mode_map Bar::PRESET_MODES = { // + {"dock", + {// Modes supported by the sway config; see man sway-bar(5) + .layer = bar_layer::BOTTOM, + .exclusive = true, + .passthrough = false, + .visible = true}}, + {"hide", + {// + .layer = bar_layer::TOP, + .exclusive = false, + .passthrough = false, + .visible = true}}, + {"invisible", + {// + .layer = bar_layer::BOTTOM, + .exclusive = false, + .passthrough = true, + .visible = false}}, + {"overlay", + {// + .layer = bar_layer::TOP, + .exclusive = false, + .passthrough = true, + .visible = true}}}; + +const std::string_view Bar::MODE_DEFAULT = "dock"; +const std::string_view Bar::MODE_INVISIBLE = "invisible"; + #ifdef HAVE_GTK_LAYER_SHELL struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { GLSSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} { @@ -533,28 +562,25 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) } } -void waybar::Bar::setMode(const std::string& mode) { - bool passthrough = false; - visible = true; - exclusive = true; - layer_ = bar_layer::BOTTOM; - - if (mode == "hide") { - exclusive = false; - layer_ = bar_layer::TOP; - visible = false; - } else if (mode == "invisible") { - visible = false; - } else if (mode == "overlay") { - exclusive = false; - layer_ = bar_layer::TOP; - passthrough = true; +void waybar::Bar::setMode(const std::string_view& mode) { + auto it = configured_modes.find(mode); + if (it != configured_modes.end()) { + last_mode_ = mode; + setMode(it->second); + } else { + spdlog::warn("Unknown mode \"{}\" requested", mode); + last_mode_ = MODE_DEFAULT; + setMode(configured_modes.at(MODE_DEFAULT)); } +} +void waybar::Bar::setMode(const struct bar_mode& mode) { + layer_ = mode.layer; + exclusive = mode.exclusive; surface_impl_->setLayer(layer_); surface_impl_->setExclusiveZone(exclusive); - surface_impl_->setPassThrough(passthrough); - setVisible(visible); + surface_impl_->setPassThrough(mode.passthrough); + setVisible(mode.visible); } void waybar::Bar::onMap(GdkEventAny*) { From ae88d7d8dcb028aea55d6c2ac675cfd22fb5db89 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 19 Nov 2021 19:31:41 -0800 Subject: [PATCH 15/66] feat(bar): use "default" mode to store global options Read `layer`, `exclusive`, `passthrough` into a special mode "default". Drop `overlay` layer hacks, as it's easier to use `"mode": "overlay"` for the same result. --- src/bar.cpp | 51 +++++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index 2f8bc27..b009110 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -24,6 +24,12 @@ static constexpr const char* SIZE_DEFINED = "{} size is defined in the config file so it will stay like that"; const Bar::bar_mode_map Bar::PRESET_MODES = { // + {"default", + {// Special mode to hold the global bar configuration + .layer = bar_layer::BOTTOM, + .exclusive = true, + .passthrough = false, + .visible = true}}, {"dock", {// Modes supported by the sway config; see man sway-bar(5) .layer = bar_layer::BOTTOM, @@ -49,7 +55,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // .passthrough = true, .visible = true}}}; -const std::string_view Bar::MODE_DEFAULT = "dock"; +const std::string_view Bar::MODE_DEFAULT = "default"; const std::string_view Bar::MODE_INVISIBLE = "invisible"; #ifdef HAVE_GTK_LAYER_SHELL @@ -517,33 +523,26 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) surface_impl_->setPosition(position); surface_impl_->setSize(width, height); + /* Init "default" mode from globals */ + auto& default_mode = configured_modes[MODE_DEFAULT]; + if (config["layer"] == "top") { + default_mode.layer = bar_layer::TOP; + } else if (config["layer"] == "overlay") { + default_mode.layer = bar_layer::OVERLAY; + } + + if (config["exclusive"].isBool()) { + default_mode.exclusive = config["exclusive"].asBool(); + } + + if (config["passthrough"].isBool()) { + default_mode.passthrough = config["passthrough"].asBool(); + } + if (auto mode = config["mode"]; mode.isString()) { - setMode(mode.asString()); + setMode(config["mode"].asString()); } else { - if (config["layer"] == "top") { - layer_ = bar_layer::TOP; - } else if (config["layer"] == "overlay") { - layer_ = bar_layer::OVERLAY; - } - - if (config["exclusive"].isBool()) { - exclusive = config["exclusive"].asBool(); - } else if (layer_ == bar_layer::OVERLAY) { - // swaybar defaults: overlay mode does not reserve an exclusive zone - exclusive = false; - } - - bool passthrough = false; - if (config["passthrough"].isBool()) { - passthrough = config["passthrough"].asBool(); - } else if (layer_ == bar_layer::OVERLAY) { - // swaybar defaults: overlay mode does not accept pointer events. - passthrough = true; - } - - surface_impl_->setLayer(layer_); - surface_impl_->setExclusiveZone(exclusive); - surface_impl_->setPassThrough(passthrough); + setMode(MODE_DEFAULT); } window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap)); From 87b43c21711e46c97972d5fe6a21aa40f298d626 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 19 Nov 2021 20:02:57 -0800 Subject: [PATCH 16/66] feat(bar): attach CSS class `mode-{mode}` to window when setting mode --- src/bar.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/bar.cpp b/src/bar.cpp index b009110..43a627a 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -562,13 +562,21 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) } void waybar::Bar::setMode(const std::string_view& mode) { + using namespace std::literals::string_literals; + + auto style = window.get_style_context(); + /* remove styles added by previous setMode calls */ + style->remove_class("mode-"s + last_mode_); + auto it = configured_modes.find(mode); if (it != configured_modes.end()) { last_mode_ = mode; + style->add_class("mode-"s + last_mode_); setMode(it->second); } else { spdlog::warn("Unknown mode \"{}\" requested", mode); last_mode_ = MODE_DEFAULT; + style->add_class("mode-"s + last_mode_); setMode(configured_modes.at(MODE_DEFAULT)); } } From 52361ed3606311e7f47ff6e6a5357dfb43069d1b Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 21 Nov 2021 11:00:57 -0800 Subject: [PATCH 17/66] refactor(bar): make setVisible switch between "default" and "invisible" modes --- include/bar.hpp | 2 -- src/bar.cpp | 29 ++++++++++++----------------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 426c55c..1d7f4a9 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -78,7 +78,6 @@ 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; @@ -96,7 +95,6 @@ class Bar { std::string last_mode_{MODE_DEFAULT}; std::unique_ptr surface_impl_; - bar_layer layer_; Gtk::Box left_; Gtk::Box center_; Gtk::Box right_; diff --git a/src/bar.cpp b/src/bar.cpp index 43a627a..a7b24a6 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -426,7 +426,6 @@ 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), @@ -582,12 +581,18 @@ void waybar::Bar::setMode(const std::string_view& mode) { } void waybar::Bar::setMode(const struct bar_mode& mode) { - layer_ = mode.layer; - exclusive = mode.exclusive; - surface_impl_->setLayer(layer_); - surface_impl_->setExclusiveZone(exclusive); + surface_impl_->setLayer(mode.layer); + surface_impl_->setExclusiveZone(mode.exclusive); surface_impl_->setPassThrough(mode.passthrough); - setVisible(mode.visible); + + if (mode.visible) { + window.get_style_context()->remove_class("hidden"); + window.set_opacity(1); + } else { + window.get_style_context()->add_class("hidden"); + window.set_opacity(0); + } + surface_impl_->commit(); } void waybar::Bar::onMap(GdkEventAny*) { @@ -600,17 +605,7 @@ void waybar::Bar::onMap(GdkEventAny*) { void waybar::Bar::setVisible(bool value) { visible = value; - if (!visible) { - window.get_style_context()->add_class("hidden"); - window.set_opacity(0); - surface_impl_->setLayer(bar_layer::BOTTOM); - } else { - window.get_style_context()->remove_class("hidden"); - window.set_opacity(1); - surface_impl_->setLayer(layer_); - } - surface_impl_->setExclusiveZone(exclusive && visible); - surface_impl_->commit(); + setMode(visible ? MODE_DEFAULT : MODE_INVISIBLE); } void waybar::Bar::toggle() { setVisible(!visible); } From 5905078e56c3810fa7129049f1689b662fa6704a Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 19 Nov 2021 21:02:29 -0800 Subject: [PATCH 18/66] doc: document `mode` option of the bar config --- man/waybar.5.scd.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 66d5b2e..8490ee5 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -72,6 +72,11 @@ 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. +*mode* ++ + typeof: string ++ + Selects one of the preconfigured display modes. This is an equivalent of the sway-bar(5) *mode* command and supports the same values: *dock*, *hide*, *invisible*, *overlay*. ++ + Note: *hide* and *invisible* modes may be not as useful without Sway IPC. + *exclusive* ++ typeof: bool ++ default: *true* unless the layer is set to *overlay* ++ From 452dcaa5d3f079cca0bf7be3896d7b9c814671ae Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 19 Nov 2021 20:28:41 -0800 Subject: [PATCH 19/66] feat(client): store bar_id argument --- include/client.hpp | 1 + src/client.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/include/client.hpp b/include/client.hpp index bd80d0b..7fc3dce 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -29,6 +29,7 @@ class Client { struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr; std::vector> bars; Config config; + std::string bar_id; private: Client() = default; diff --git a/src/client.cpp b/src/client.cpp index 95f5a29..8adbeac 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -199,7 +199,6 @@ int waybar::Client::main(int argc, char *argv[]) { bool show_version = false; 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") | From 23e5181cace3c6e6099ce539379fafd653addbbf Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Oct 2020 19:34:48 -0700 Subject: [PATCH 20/66] feat(swaybar-ipc): add swaybar IPC client --- include/bar.hpp | 19 +++++++- include/modules/sway/bar.hpp | 48 +++++++++++++++++++ meson.build | 1 + src/bar.cpp | 17 +++++++ src/modules/sway/bar.cpp | 92 ++++++++++++++++++++++++++++++++++++ 5 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 include/modules/sway/bar.hpp create mode 100644 src/modules/sway/bar.cpp diff --git a/include/bar.hpp b/include/bar.hpp index 1d7f4a9..1b9617b 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -8,6 +8,9 @@ #include #include +#include +#include + #include "AModule.hpp" #include "xdg-output-unstable-v1-client-protocol.h" @@ -43,6 +46,12 @@ struct bar_mode { bool visible; }; +#ifdef HAVE_SWAY +namespace modules::sway { +class BarIpcClient; +} +#endif // HAVE_SWAY + class BarSurface { protected: BarSurface() = default; @@ -68,7 +77,7 @@ class Bar { Bar(struct waybar_output *w_output, const Json::Value &); Bar(const Bar &) = delete; - ~Bar() = default; + ~Bar(); void setMode(const std::string_view &); void setVisible(bool visible); @@ -82,6 +91,10 @@ class Bar { bool vertical = false; Gtk::Window window; +#ifdef HAVE_SWAY + std::string bar_id; +#endif + private: void onMap(GdkEventAny *); auto setupWidgets() -> void; @@ -102,6 +115,10 @@ class Bar { std::vector> modules_left_; std::vector> modules_center_; std::vector> modules_right_; +#ifdef HAVE_SWAY + using BarIpcClient = modules::sway::BarIpcClient; + std::unique_ptr _ipc_client; +#endif }; } // namespace waybar diff --git a/include/modules/sway/bar.hpp b/include/modules/sway/bar.hpp new file mode 100644 index 0000000..e32dee4 --- /dev/null +++ b/include/modules/sway/bar.hpp @@ -0,0 +1,48 @@ +#pragma once +#include + +#include "modules/sway/ipc/client.hpp" +#include "util/SafeSignal.hpp" +#include "util/json.hpp" + +namespace waybar { + +class Bar; + +namespace modules::sway { + +/* + * Supported subset of i3/sway IPC barconfig object + */ +struct swaybar_config { + std::string id; + std::string mode; + std::string hidden_state; + std::string position; +}; + +/** + * swaybar IPC client + */ +class BarIpcClient { + public: + BarIpcClient(waybar::Bar& bar); + + private: + void onInitialConfig(const struct Ipc::ipc_response& res); + void onIpcEvent(const struct Ipc::ipc_response&); + void onConfigUpdate(const swaybar_config& config); + void onVisibilityUpdate(bool visible_by_modifier); + + Bar& bar_; + util::JsonParser parser_; + Ipc ipc_; + + swaybar_config bar_config_; + + SafeSignal signal_visible_; + SafeSignal signal_config_; +}; + +} // namespace modules::sway +} // namespace waybar diff --git a/meson.build b/meson.build index 52c9d29..a15ef59 100644 --- a/meson.build +++ b/meson.build @@ -177,6 +177,7 @@ endif add_project_arguments('-DHAVE_SWAY', language: 'cpp') src_files += [ 'src/modules/sway/ipc/client.cpp', + 'src/modules/sway/bar.cpp', 'src/modules/sway/mode.cpp', 'src/modules/sway/language.cpp', 'src/modules/sway/window.cpp', diff --git a/src/bar.cpp b/src/bar.cpp index a7b24a6..8ad81e9 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -11,6 +11,10 @@ #include "factory.hpp" #include "wlr-layer-shell-unstable-v1-client-protocol.h" +#ifdef HAVE_SWAY +#include "modules/sway/bar.hpp" +#endif + namespace waybar { static constexpr const char* MIN_HEIGHT_MSG = "Requested height: {} is less than the minimum height: {} required by the modules"; @@ -546,6 +550,16 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap)); +#if HAVE_SWAY + if (auto ipc = config["ipc"]; ipc.isBool() && ipc.asBool()) { + bar_id = Client::inst()->bar_id; + if (auto id = config["id"]; id.isString()) { + bar_id = id.asString(); + } + _ipc_client = std::make_unique(*this); + } +#endif + setupWidgets(); window.show_all(); @@ -560,6 +574,9 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) } } +/* Need to define it here because of forward declared members */ +waybar::Bar::~Bar() = default; + void waybar::Bar::setMode(const std::string_view& mode) { using namespace std::literals::string_literals; diff --git a/src/modules/sway/bar.cpp b/src/modules/sway/bar.cpp new file mode 100644 index 0000000..762353c --- /dev/null +++ b/src/modules/sway/bar.cpp @@ -0,0 +1,92 @@ +#include "modules/sway/bar.hpp" + +#include +#include + +#include "bar.hpp" +#include "modules/sway/ipc/ipc.hpp" + +namespace waybar::modules::sway { + +BarIpcClient::BarIpcClient(waybar::Bar& bar) : bar_{bar} { + { + sigc::connection handle = + ipc_.signal_cmd.connect(sigc::mem_fun(*this, &BarIpcClient::onInitialConfig)); + ipc_.sendCmd(IPC_GET_BAR_CONFIG, bar_.bar_id); + + handle.disconnect(); + } + + signal_config_.connect(sigc::mem_fun(*this, &BarIpcClient::onConfigUpdate)); + signal_visible_.connect(sigc::mem_fun(*this, &BarIpcClient::onVisibilityUpdate)); + + ipc_.subscribe(R"(["bar_state_update", "barconfig_update"])"); + ipc_.signal_event.connect(sigc::mem_fun(*this, &BarIpcClient::onIpcEvent)); + // Launch worker + ipc_.setWorker([this] { + try { + ipc_.handleEvent(); + } catch (const std::exception& e) { + spdlog::error("BarIpcClient::handleEvent {}", e.what()); + } + }); +} + +struct swaybar_config parseConfig(const Json::Value& payload) { + swaybar_config conf; + if (auto id = payload["id"]; id.isString()) { + conf.id = id.asString(); + } + if (auto mode = payload["mode"]; mode.isString()) { + conf.mode = mode.asString(); + } + if (auto hs = payload["hidden_state"]; hs.isString()) { + conf.hidden_state = hs.asString(); + } + if (auto position = payload["position"]; position.isString()) { + conf.position = position.asString(); + } + return conf; +} + +void BarIpcClient::onInitialConfig(const struct Ipc::ipc_response& res) { + try { + auto payload = parser_.parse(res.payload); + auto config = parseConfig(payload); + onConfigUpdate(config); + } catch (const std::exception& e) { + spdlog::error("BarIpcClient::onInitialConfig {}", e.what()); + } +} + +void BarIpcClient::onIpcEvent(const struct Ipc::ipc_response& res) { + try { + auto payload = parser_.parse(res.payload); + if (auto id = payload["id"]; id.isString() && id.asString() != bar_.bar_id) { + spdlog::trace("swaybar ipc: ignore event for {}", id.asString()); + return; + } + if (payload.isMember("visible_by_modifier")) { + // visibility change for hidden bar + signal_visible_(payload["visible_by_modifier"].asBool()); + } else { + // configuration update + auto config = parseConfig(payload); + signal_config_(config); + } + } catch (const std::exception& e) { + spdlog::error("BarIpcClient::onEvent {}", e.what()); + } +} + +void BarIpcClient::onConfigUpdate(const swaybar_config& config) { + spdlog::info("config update: {} {} {}", config.id, config.mode, config.position); + // TODO: pass config to bars +} + +void BarIpcClient::onVisibilityUpdate(bool visible_by_modifier) { + spdlog::trace("visiblity update: {}", visible_by_modifier); + // TODO: pass visibility to bars +} + +} // namespace waybar::modules::sway From bc134531552b7429e55337fdfb1734a3e8701551 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 15 Sep 2021 22:39:51 +0700 Subject: [PATCH 21/66] feat(swaybar-ipc): handle mode update --- src/modules/sway/bar.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/sway/bar.cpp b/src/modules/sway/bar.cpp index 762353c..3179221 100644 --- a/src/modules/sway/bar.cpp +++ b/src/modules/sway/bar.cpp @@ -81,7 +81,8 @@ void BarIpcClient::onIpcEvent(const struct Ipc::ipc_response& res) { void BarIpcClient::onConfigUpdate(const swaybar_config& config) { spdlog::info("config update: {} {} {}", config.id, config.mode, config.position); - // TODO: pass config to bars + bar_config_ = config; + bar_.setMode(bar_config_.mode); } void BarIpcClient::onVisibilityUpdate(bool visible_by_modifier) { From ebdeb8670310e1e4308a457be04829ba11d2b156 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Oct 2020 19:35:55 -0700 Subject: [PATCH 22/66] feat(swaybar-ipc): handle visibility_by_modifier update --- include/modules/sway/bar.hpp | 2 ++ src/modules/sway/bar.cpp | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/modules/sway/bar.hpp b/include/modules/sway/bar.hpp index e32dee4..d3a697c 100644 --- a/include/modules/sway/bar.hpp +++ b/include/modules/sway/bar.hpp @@ -33,12 +33,14 @@ class BarIpcClient { void onIpcEvent(const struct Ipc::ipc_response&); void onConfigUpdate(const swaybar_config& config); void onVisibilityUpdate(bool visible_by_modifier); + void update(); Bar& bar_; util::JsonParser parser_; Ipc ipc_; swaybar_config bar_config_; + bool visible_by_modifier_ = false; SafeSignal signal_visible_; SafeSignal signal_config_; diff --git a/src/modules/sway/bar.cpp b/src/modules/sway/bar.cpp index 3179221..7cae4ba 100644 --- a/src/modules/sway/bar.cpp +++ b/src/modules/sway/bar.cpp @@ -82,12 +82,23 @@ void BarIpcClient::onIpcEvent(const struct Ipc::ipc_response& res) { void BarIpcClient::onConfigUpdate(const swaybar_config& config) { spdlog::info("config update: {} {} {}", config.id, config.mode, config.position); bar_config_ = config; - bar_.setMode(bar_config_.mode); + update(); } void BarIpcClient::onVisibilityUpdate(bool visible_by_modifier) { spdlog::trace("visiblity update: {}", visible_by_modifier); - // TODO: pass visibility to bars + visible_by_modifier_ = visible_by_modifier; + update(); +} + +void BarIpcClient::update() { + bool visible = visible_by_modifier_; + if (bar_config_.mode == "invisible") { + visible = false; + } else if (bar_config_.mode != "hide" || bar_config_.hidden_state != "hide") { + visible = true; + } + bar_.setMode(visible ? bar_config_.mode : Bar::MODE_INVISIBLE); } } // namespace waybar::modules::sway From 9d9f95976926d5817837d03344b9506d700cee16 Mon Sep 17 00:00:00 2001 From: Ciaran Downey Date: Wed, 25 Aug 2021 14:47:51 -0700 Subject: [PATCH 23/66] 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 24/66] 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 8d04da1551bdfae54d17e16c6e49e7e0819e1f71 Mon Sep 17 00:00:00 2001 From: WuerfelDev Date: Tue, 21 Sep 2021 13:15:36 +0200 Subject: [PATCH 25/66] 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 110c66dd329427fb29169fe0389f3ee4cbbd8307 Mon Sep 17 00:00:00 2001 From: Sergey Mishin Date: Sun, 3 Oct 2021 16:48:21 +0000 Subject: [PATCH 26/66] Refactor Clock: generalize multi timezones and single timezone cases After this refactoring: 1. Timezones parses only once on start and the we refer to saved values. All time_zone.isString() checks gone to the constructor. 2. Single timezone case handling as case of multi timezoned logic. 3. Scroll event seems more clear now. 4. Tooltip template parses on start to check if there calendar placeholder or not. To do not calculate calendar_text() if not necessary. --- include/modules/clock.hpp | 11 ++-- src/modules/clock.cpp | 108 ++++++++++++++++++++++++-------------- 2 files changed, 75 insertions(+), 44 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 17752e4..9f95019 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -17,6 +17,8 @@ struct waybar_time { date::zoned_seconds ztime; }; +const std::string kCalendarPlaceholder = "calendar"; + class Clock : public ALabel { public: Clock(const std::string&, const Json::Value&); @@ -26,18 +28,19 @@ class Clock : public ALabel { private: util::SleeperThread thread_; std::locale locale_; - const date::time_zone* time_zone_; - bool fixed_time_zone_; - int time_zone_idx_; + std::vector time_zones_; + int current_time_zone_idx_; date::year_month_day cached_calendar_ymd_ = date::January/1/0; std::string cached_calendar_text_; + bool is_calendar_in_tooltip_; 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; - bool setTimeZone(Json::Value zone_name); + const date::time_zone* current_timezone(); + bool is_timezone_fixed(); }; } // namespace waybar::modules diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 7c94c45..d925c2b 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -14,17 +14,51 @@ 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) { + : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), + current_time_zone_idx_(0), + is_calendar_in_tooltip_(false) +{ if (config_["timezones"].isArray() && !config_["timezones"].empty()) { - time_zone_idx_ = 0; - setTimeZone(config_["timezones"][time_zone_idx_]); + for (const auto& zone_name: config_["timezones"]) { + if (!zone_name.isString() || zone_name.asString().empty()) { + time_zones_.push_back(nullptr); + continue; + } + time_zones_.push_back( + date::locate_zone( + zone_name.asString() + ) + ); + + } + // If we parse all timezones and no one is good, add nullptr to the tmezones vector, to mark that we need to show localtime + if (!time_zones_.size()) { + time_zones_.push_back(nullptr); + } } else { - setTimeZone(config_["timezone"]); + time_zones_.push_back( + date::locate_zone( + config_["timezone"].asString() + ) + ); } - if (fixed_time_zone_) { + + if (!is_timezone_fixed()) { spdlog::warn("As using a timezone, some format args may be missing as the date library haven't got a release since 2018."); } + // Check if we have to particular placeholder in tooltip format, to know what to calculate on update + if (config_["tooltip-format"].isString()) { + std::string trimmedFormat = config_["tooltip-format"].asString(); + trimmedFormat.erase(std::remove_if(trimmedFormat.begin(), + trimmedFormat.end(), + [](unsigned char x){return std::isspace(x);}), + trimmedFormat.end()); + if (trimmedFormat.find("{" + kCalendarPlaceholder + "}") != std::string::npos) { + is_calendar_in_tooltip_ = true; + } + } + if (config_["locale"].isString()) { locale_ = std::locale(config_["locale"].asString()); } else { @@ -40,53 +74,46 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) }; } +const date::time_zone* waybar::modules::Clock::current_timezone() { + return time_zones_[current_time_zone_idx_] ? time_zones_[current_time_zone_idx_] : date::current_zone(); +} + +bool waybar::modules::Clock::is_timezone_fixed() { + return time_zones_[current_time_zone_idx_] != nullptr; +} + auto waybar::modules::Clock::update() -> void { - if (!fixed_time_zone_) { - // Time zone can change. Be sure to pick that. - time_zone_ = date::current_zone(); - } - - auto now = std::chrono::system_clock::now(); + auto time_zone = current_timezone(); + auto now = std::chrono::system_clock::now(); waybar_time wtime = {locale_, - date::make_zoned(time_zone_, date::floor(now))}; - - std::string text; - if (!fixed_time_zone_) { + date::make_zoned(time_zone, date::floor(now))}; + std::string text = ""; + if (!is_timezone_fixed()) { // As date dep is not fully compatible, prefer fmt tzset(); auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); text = fmt::format(format_, localtime); - label_.set_markup(text); } else { text = fmt::format(format_, wtime); - label_.set_markup(text); } + label_.set_markup(text); if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { - const auto calendar = calendar_text(wtime); - auto tooltip_format = config_["tooltip-format"].asString(); - auto tooltip_text = fmt::format(tooltip_format, wtime, fmt::arg("calendar", calendar)); - label_.set_tooltip_markup(tooltip_text); - } else { - label_.set_tooltip_markup(text); + std::string calendarText = ""; + if (is_calendar_in_tooltip_) { + calendarText = calendar_text(wtime); + } + auto tooltip_format = config_["tooltip-format"].asString(); + text = fmt::format(tooltip_format, wtime, fmt::arg("calendar", calendarText)); } } + + label_.set_tooltip_markup(text); // Call parent update 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()) { @@ -97,17 +124,18 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) { if (dir != SCROLL_DIR::UP && dir != SCROLL_DIR::DOWN) { return true; } - if (!config_["timezones"].isArray() || config_["timezones"].empty()) { + if (time_zones_.size() == 1) { return true; } - auto nr_zones = config_["timezones"].size(); + + auto nr_zones = time_zones_.size(); if (dir == SCROLL_DIR::UP) { - size_t new_idx = time_zone_idx_ + 1; - time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx; + size_t new_idx = current_time_zone_idx_ + 1; + current_time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx; } else { - time_zone_idx_ = time_zone_idx_ == 0 ? nr_zones - 1 : time_zone_idx_ - 1; + current_time_zone_idx_ = current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1; } - setTimeZone(config_["timezones"][time_zone_idx_]); + update(); return true; } From d8bc6c92bb49f4255dab1c24c88f1e9c1fecd549 Mon Sep 17 00:00:00 2001 From: Sergey Mishin Date: Tue, 5 Oct 2021 09:55:30 +0000 Subject: [PATCH 27/66] Fix style and spelling --- src/modules/clock.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index d925c2b..33028b7 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -31,7 +31,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) ); } - // If we parse all timezones and no one is good, add nullptr to the tmezones vector, to mark that we need to show localtime + // If all timezones are parsed and no one is good, add nullptr to the timezones vector, to mark that local time should be shown. if (!time_zones_.size()) { time_zones_.push_back(nullptr); } @@ -47,14 +47,14 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) spdlog::warn("As using a timezone, some format args may be missing as the date library haven't got a release since 2018."); } - // Check if we have to particular placeholder in tooltip format, to know what to calculate on update + // Check if a particular placeholder is present in the tooltip format, to know what to calculate on update. if (config_["tooltip-format"].isString()) { - std::string trimmedFormat = config_["tooltip-format"].asString(); - trimmedFormat.erase(std::remove_if(trimmedFormat.begin(), - trimmedFormat.end(), + std::string trimmed_format = config_["tooltip-format"].asString(); + trimmed_format.erase(std::remove_if(trimmed_format.begin(), + trimmed_format.end(), [](unsigned char x){return std::isspace(x);}), - trimmedFormat.end()); - if (trimmedFormat.find("{" + kCalendarPlaceholder + "}") != std::string::npos) { + trimmed_format.end()); + if (trimmed_format.find("{" + kCalendarPlaceholder + "}") != std::string::npos) { is_calendar_in_tooltip_ = true; } } @@ -100,12 +100,12 @@ auto waybar::modules::Clock::update() -> void { if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { - std::string calendarText = ""; + std::string calendar_lines = ""; if (is_calendar_in_tooltip_) { - calendarText = calendar_text(wtime); + calendar_lines = calendar_text(wtime); } auto tooltip_format = config_["tooltip-format"].asString(); - text = fmt::format(tooltip_format, wtime, fmt::arg("calendar", calendarText)); + text = fmt::format(tooltip_format, wtime, fmt::arg("calendar", calendar_lines)); } } From c5e4d2632045a42d620d929a976a0263fbdc047c Mon Sep 17 00:00:00 2001 From: Sergey Mishin Date: Tue, 5 Oct 2021 10:20:06 +0000 Subject: [PATCH 28/66] Fix working without timezone --- src/modules/clock.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 33028b7..7e7d742 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -29,13 +29,8 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) zone_name.asString() ) ); - } - // If all timezones are parsed and no one is good, add nullptr to the timezones vector, to mark that local time should be shown. - if (!time_zones_.size()) { - time_zones_.push_back(nullptr); - } - } else { + } else if (config_["timezone"].isString() && !config_["timezone"].asString().empty()) { time_zones_.push_back( date::locate_zone( config_["timezone"].asString() @@ -43,6 +38,11 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) ); } + // If all timezones are parsed and no one is good, add nullptr to the timezones vector, to mark that local time should be shown. + if (!time_zones_.size()) { + time_zones_.push_back(nullptr); + } + if (!is_timezone_fixed()) { spdlog::warn("As using a timezone, some format args may be missing as the date library haven't got a release since 2018."); } @@ -105,7 +105,7 @@ auto waybar::modules::Clock::update() -> void { calendar_lines = calendar_text(wtime); } auto tooltip_format = config_["tooltip-format"].asString(); - text = fmt::format(tooltip_format, wtime, fmt::arg("calendar", calendar_lines)); + text = fmt::format(tooltip_format, wtime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines)); } } From 75a6dddea5ae5f96d40e0b823ee65bf94ef329f6 Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 20 Oct 2021 01:23:00 +0300 Subject: [PATCH 29/66] 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 30/66] 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 31/66] 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 32/66] 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 33/66] 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 34/66] 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 35/66] 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 36/66] 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 37/66] 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 38/66] 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 39/66] 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 40/66] 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 41/66] 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 42/66] 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 5baffbf8f8b47b619fc59334caeb0adf6e9473f0 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 19 Nov 2021 20:28:57 -0800 Subject: [PATCH 43/66] doc: document swaybar ipc options, `ipc` and `id` --- man/waybar.5.scd.in | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 8490ee5..bfc9ab8 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -94,6 +94,16 @@ 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. +*ipc* ++ + typeof: bool ++ + default: false ++ + Option to subscribe to the Sway IPC bar configuration and visibility events and control waybar with *swaymsg bar* commands. ++ + Requires *bar_id* value from sway configuration to be either passed with the *-b* commandline argument or specified with the *id* option. + +*id* ++ + typeof: string ++ + *bar_id* for the Sway IPC. Use this if you need to override the value passed with the *-b bar_id* commandline argument for the specific bar instance. + *include* ++ typeof: string|array ++ Paths to additional configuration files. From 4154492603ddec3fc8e091cf34bcbc8081fa780a Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 19 Nov 2021 20:35:51 -0800 Subject: [PATCH 44/66] 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 45/66] 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 46/66] 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 6bfb674d1b034a9679c049668714532e2da880c1 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 21 Nov 2021 17:28:47 -0800 Subject: [PATCH 47/66] fix(swaybar-ipc): better logs --- include/modules/sway/bar.hpp | 1 - src/modules/sway/bar.cpp | 11 ++++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/modules/sway/bar.hpp b/include/modules/sway/bar.hpp index d3a697c..c4381a4 100644 --- a/include/modules/sway/bar.hpp +++ b/include/modules/sway/bar.hpp @@ -18,7 +18,6 @@ struct swaybar_config { std::string id; std::string mode; std::string hidden_state; - std::string position; }; /** diff --git a/src/modules/sway/bar.cpp b/src/modules/sway/bar.cpp index 7cae4ba..17382e3 100644 --- a/src/modules/sway/bar.cpp +++ b/src/modules/sway/bar.cpp @@ -43,9 +43,6 @@ struct swaybar_config parseConfig(const Json::Value& payload) { if (auto hs = payload["hidden_state"]; hs.isString()) { conf.hidden_state = hs.asString(); } - if (auto position = payload["position"]; position.isString()) { - conf.position = position.asString(); - } return conf; } @@ -80,13 +77,17 @@ void BarIpcClient::onIpcEvent(const struct Ipc::ipc_response& res) { } void BarIpcClient::onConfigUpdate(const swaybar_config& config) { - spdlog::info("config update: {} {} {}", config.id, config.mode, config.position); + spdlog::info("config update for {}: id {}, mode {}, hidden_state {}", + bar_.bar_id, + config.id, + config.mode, + config.hidden_state); bar_config_ = config; update(); } void BarIpcClient::onVisibilityUpdate(bool visible_by_modifier) { - spdlog::trace("visiblity update: {}", visible_by_modifier); + spdlog::debug("visiblity update for {}: {}", bar_.bar_id, visible_by_modifier); visible_by_modifier_ = visible_by_modifier; update(); } From 23991b65430c8b06e360ef289c0db6d9f3e297ec Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 23 Nov 2021 03:10:44 +0300 Subject: [PATCH 48/66] 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 49/66] 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 50/66] 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); } } } From 2290fe10aa35a2c32953361df81a4618d55ee148 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 23 Nov 2021 08:46:58 -0800 Subject: [PATCH 51/66] fix(bar): handle ipc connection errors. Try to use the default bar id (`bar-0`) if none is set. --- src/bar.cpp | 10 +++++++++- src/modules/sway/bar.cpp | 14 ++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index 8ad81e9..26d64ec 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -61,6 +61,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // const std::string_view Bar::MODE_DEFAULT = "default"; const std::string_view Bar::MODE_INVISIBLE = "invisible"; +const std::string_view DEFAULT_BAR_ID = "bar-0"; #ifdef HAVE_GTK_LAYER_SHELL struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { @@ -556,7 +557,14 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) if (auto id = config["id"]; id.isString()) { bar_id = id.asString(); } - _ipc_client = std::make_unique(*this); + if (bar_id.empty()) { + bar_id = DEFAULT_BAR_ID; + } + try { + _ipc_client = std::make_unique(*this); + } catch (const std::exception& exc) { + spdlog::warn("Failed to open bar ipc connection: {}", exc.what()); + } } #endif diff --git a/src/modules/sway/bar.cpp b/src/modules/sway/bar.cpp index 17382e3..78e524a 100644 --- a/src/modules/sway/bar.cpp +++ b/src/modules/sway/bar.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "bar.hpp" #include "modules/sway/ipc/ipc.hpp" @@ -47,13 +49,13 @@ struct swaybar_config parseConfig(const Json::Value& payload) { } void BarIpcClient::onInitialConfig(const struct Ipc::ipc_response& res) { - try { - auto payload = parser_.parse(res.payload); - auto config = parseConfig(payload); - onConfigUpdate(config); - } catch (const std::exception& e) { - spdlog::error("BarIpcClient::onInitialConfig {}", e.what()); + auto payload = parser_.parse(res.payload); + if (auto success = payload.get("success", true); !success.asBool()) { + auto err = payload.get("error", "Unknown error"); + throw std::runtime_error(err.asString()); } + auto config = parseConfig(payload); + onConfigUpdate(config); } void BarIpcClient::onIpcEvent(const struct Ipc::ipc_response& res) { From 0472d279e4ecb1ffb5d44e763f7b6a19b3d5e079 Mon Sep 17 00:00:00 2001 From: kraftwerk28 Date: Tue, 23 Nov 2021 14:15:55 +0200 Subject: [PATCH 52/66] Add {flag} format replacement --- include/modules/sway/language.hpp | 8 +++++++- src/modules/sway/language.cpp | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index 1faf52b..1944166 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -32,6 +32,12 @@ class Language : public ALabel, public sigc::trackable { std::string short_name; std::string variant; std::string short_description; + std::string country_flag() const { + static std::string result = "\xf0\x9f\x87\xff\xf0\x9f\x87\xff"; + result[3] = short_name[0] - 0xbb; + result[7] = short_name[1] - 0xbb; + return result; + } }; class XKBContext { @@ -54,7 +60,7 @@ class Language : public ALabel, public sigc::trackable { const static std::string XKB_LAYOUT_NAMES_KEY; const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY; - + Layout layout_; std::string tooltip_format_ = ""; std::map layouts_map_; diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 186fa4b..bdd4b99 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -99,7 +99,8 @@ auto Language::update() -> void { fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), - fmt::arg("variant", layout_.variant))); + fmt::arg("variant", layout_.variant), + fmt::arg("flag", layout_.country_flag()))); label_.set_markup(display_layout); if (tooltipEnabled()) { if (tooltip_format_ != "") { From 02560a653776db9c43bb0dbdbfa998e716a6054a Mon Sep 17 00:00:00 2001 From: kraftwerk28 Date: Wed, 25 Aug 2021 23:18:56 +0300 Subject: [PATCH 53/66] Update manpage --- man/waybar-sway-language.5.scd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index 92a647e..1c88314 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -37,6 +37,8 @@ Addressed by *sway/language* *{variant}*: Variant of layout (e.g. "dvorak"). +*{flag}*: Country flag of layout. + # EXAMPLES ``` From 59040c53e4ea73bfa654fb28ff8e4644595fa1ab Mon Sep 17 00:00:00 2001 From: kraftwerk28 Date: Wed, 25 Aug 2021 23:26:04 +0300 Subject: [PATCH 54/66] Move definition to .cpp --- include/modules/sway/language.hpp | 7 +------ src/modules/sway/language.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index 1944166..92e2bba 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -32,12 +32,7 @@ class Language : public ALabel, public sigc::trackable { std::string short_name; std::string variant; std::string short_description; - std::string country_flag() const { - static std::string result = "\xf0\x9f\x87\xff\xf0\x9f\x87\xff"; - result[3] = short_name[0] - 0xbb; - result[7] = short_name[1] - 0xbb; - return result; - } + std::string country_flag() const; }; class XKBContext { diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index bdd4b99..1b12ab0 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -213,4 +213,11 @@ Language::XKBContext::~XKBContext() { rxkb_context_unref(context_); delete layout_; } + +std::string Language::Layout::country_flag() const { + static std::string result = "\xf0\x9f\x87\xff\xf0\x9f\x87\xff"; + result[3] = short_name[0] - 0xbb; + result[7] = short_name[1] - 0xbb; + return result; +} } // namespace waybar::modules::sway From 89afa8e149e3ed7d45ef8a93fb1505bf01cd6a17 Mon Sep 17 00:00:00 2001 From: kraftwerk28 Date: Wed, 24 Nov 2021 02:13:40 +0200 Subject: [PATCH 55/66] Checking if emoji byte doesn't get out of bounds --- src/modules/sway/language.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 1b12ab0..73a64c3 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -108,7 +108,8 @@ auto Language::update() -> void { fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), - fmt::arg("variant", layout_.variant))); + fmt::arg("variant", layout_.variant), + fmt::arg("flag", layout_.country_flag()))); label_.set_tooltip_markup(tooltip_display_layout); } else { label_.set_tooltip_markup(display_layout); @@ -215,9 +216,13 @@ Language::XKBContext::~XKBContext() { } std::string Language::Layout::country_flag() const { - static std::string result = "\xf0\x9f\x87\xff\xf0\x9f\x87\xff"; - result[3] = short_name[0] - 0xbb; - result[7] = short_name[1] - 0xbb; - return result; + if (short_name.size() != 2) return ""; + unsigned char result[] = "\xf0\x9f\x87\x00\xf0\x9f\x87\x00"; + result[3] = short_name[0] + 0x45; + result[7] = short_name[1] + 0x45; + // Check if both emojis are in A-Z symbol bounds + if (result[3] < 0xa6 || result[3] > 0xbf) return ""; + if (result[7] < 0xa6 || result[7] > 0xbf) return ""; + return std::string{reinterpret_cast(result)}; } } // namespace waybar::modules::sway From 8fe42ebd2eac1b6dbe79a18a5fe4ead9a78bac93 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 23 Nov 2021 19:18:24 -0800 Subject: [PATCH 56/66] doc: update `exclusive` and `passthrough` defaults --- man/waybar.5.scd.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index bfc9ab8..92d0774 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -79,12 +79,12 @@ Also a minimal example configuration can be found on the at the bottom of this m *exclusive* ++ typeof: bool ++ - default: *true* unless the layer is set to *overlay* ++ + default: *true* ++ 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* ++ + default: *false* ++ 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. From b4e19678b7f0a408c80d1ef89e4eb2d8e2adbf36 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 23 Nov 2021 19:48:31 -0800 Subject: [PATCH 57/66] ci: increase FreeBSD VM memory to 2048MB Intermittent CI failures without any useful diagnostics could be caused by the OOM killer. 1024MB is not really enough to run 3 parallel jobs with a modern C++ compiler. --- .github/workflows/freebsd.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 03e5d70..d5064fe 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -13,6 +13,7 @@ jobs: - name: Test in FreeBSD VM uses: vmactions/freebsd-vm@v0.1.5 # aka FreeBSD 13.0 with: + mem: 2048 usesh: true prepare: | export CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib # sndio From 0c18e57937376b924203e6dc526fad6fa6f93fe4 Mon Sep 17 00:00:00 2001 From: Nicolas Joyard Date: Sun, 31 Oct 2021 23:55:13 +0100 Subject: [PATCH 58/66] add group feature --- include/bar.hpp | 3 ++- include/group.hpp | 21 ++++++++++++++++++ man/waybar.5.scd.in | 22 +++++++++++++++++++ meson.build | 1 + src/bar.cpp | 53 +++++++++++++++++++++++++-------------------- src/group.cpp | 19 ++++++++++++++++ 6 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 include/group.hpp create mode 100644 src/group.cpp diff --git a/include/bar.hpp b/include/bar.hpp index 6f3dfcf..4bd5ef3 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -73,7 +73,7 @@ class Bar { private: void onMap(GdkEventAny *); auto setupWidgets() -> void; - void getModules(const Factory &, const std::string &); + void getModules(const Factory &, const std::string &, Gtk::Box*); void setupAltFormatKeyForModule(const std::string &module_name); void setupAltFormatKeyForModuleList(const char *module_list_name); @@ -86,6 +86,7 @@ class Bar { std::vector> modules_left_; std::vector> modules_center_; std::vector> modules_right_; + std::vector> modules_all_; }; } // namespace waybar diff --git a/include/group.hpp b/include/group.hpp new file mode 100644 index 0000000..f282f9c --- /dev/null +++ b/include/group.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include "AModule.hpp" +#include "bar.hpp" +#include "factory.hpp" + +namespace waybar { + +class Group : public AModule { + public: + Group(const std::string&, const Bar&, const Json::Value&); + ~Group() = default; + auto update() -> void; + operator Gtk::Widget &(); + Gtk::Box box; +}; + +} // namespace waybar diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 66d5b2e..f374953 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -203,6 +203,28 @@ When positioning Waybar on the left or right side of the screen, sometimes it's Valid options for the "rotate" property are: 0, 90, 180 and 270. +## Grouping modules + +Module groups allow stacking modules in the direction orthogonal to the bar direction. When the bar is positioned on the top or bottom of the screen, modules in a group are stacked vertically. Likewise, when positioned on the left or right, modules in a group are stacked horizontally. + +A module group is defined by specifying a module named "group/some-group-name". The group must also be configured with a list of contained modules. Example: + +``` +{ + "modules-right": ["group/hardware", "clock"], + + "group/hardware": { + "modules": [ + "cpu", + "memory", + "battery" + ] + }, + + ... +} +``` + # SUPPORTED MODULES - *waybar-backlight(5)* diff --git a/meson.build b/meson.build index 62ac8e3..c499c68 100644 --- a/meson.build +++ b/meson.build @@ -150,6 +150,7 @@ src_files = files( 'src/bar.cpp', 'src/client.cpp', 'src/config.cpp', + 'src/group.cpp', 'src/util/ustring_clen.cpp' ) diff --git a/src/bar.cpp b/src/bar.cpp index a8b230e..dee81a3 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -9,6 +9,7 @@ #include "bar.hpp" #include "client.hpp" #include "factory.hpp" +#include "group.hpp" #include "wlr-layer-shell-unstable-v1-client-protocol.h" namespace waybar { @@ -594,19 +595,7 @@ void waybar::Bar::setupAltFormatKeyForModuleList(const char* module_list_name) { } void waybar::Bar::handleSignal(int signal) { - for (auto& module : modules_left_) { - auto* custom = dynamic_cast(module.get()); - if (custom != nullptr) { - custom->refresh(signal); - } - } - for (auto& module : modules_center_) { - auto* custom = dynamic_cast(module.get()); - if (custom != nullptr) { - custom->refresh(signal); - } - } - for (auto& module : modules_right_) { + for (auto& module : modules_all_) { auto* custom = dynamic_cast(module.get()); if (custom != nullptr) { custom->refresh(signal); @@ -614,19 +603,35 @@ void waybar::Bar::handleSignal(int signal) { } } -void waybar::Bar::getModules(const Factory& factory, const std::string& pos) { - if (config[pos].isArray()) { - for (const auto& name : config[pos]) { +void waybar::Bar::getModules(const Factory& factory, const std::string& pos, Gtk::Box* group = nullptr) { + auto module_list = group ? config[pos]["modules"] : config[pos]; + if (module_list.isArray()) { + for (const auto& name : module_list) { try { - auto module = factory.makeModule(name.asString()); - if (pos == "modules-left") { - modules_left_.emplace_back(module); + auto ref = name.asString(); + AModule* module; + + if (ref.compare(0, 6, "group/") == 0 && ref.size() > 6) { + auto group_module = new waybar::Group(ref, *this, config[ref]); + getModules(factory, ref, &group_module->box); + module = group_module; + } else { + module = factory.makeModule(ref); } - if (pos == "modules-center") { - modules_center_.emplace_back(module); - } - if (pos == "modules-right") { - modules_right_.emplace_back(module); + + modules_all_.emplace_back(module); + if (group) { + group->pack_start(*module, false, false); + } else { + if (pos == "modules-left") { + modules_left_.emplace_back(module); + } + if (pos == "modules-center") { + modules_center_.emplace_back(module); + } + if (pos == "modules-right") { + modules_right_.emplace_back(module); + } } module->dp.connect([module, &name] { try { diff --git a/src/group.cpp b/src/group.cpp new file mode 100644 index 0000000..9d2188c --- /dev/null +++ b/src/group.cpp @@ -0,0 +1,19 @@ +#include "group.hpp" +#include +#include + +namespace waybar { + +Group::Group(const std::string& name, const Bar& bar, const Json::Value& config) + : AModule(config, name, "", false, false), + box{bar.vertical ? Gtk::ORIENTATION_HORIZONTAL : Gtk::ORIENTATION_VERTICAL, 0} + { +} + +auto Group::update() -> void { + // noop +} + +Group::operator Gtk::Widget&() { return box; } + +} // namespace waybar From d5112678c357818ac8a357a5d366083cd78ebaee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABlys=20Bras=20de=20fer?= Date: Sat, 27 Nov 2021 23:07:26 +0100 Subject: [PATCH 59/66] mediaplayer.py: Exit properly on SIGPIPE --- resources/custom_modules/mediaplayer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index fa9aa58..1630d97 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -110,6 +110,7 @@ def main(): signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) + signal.signal(signal.SIGPIPE, signal.SIG_DFL) for player in manager.props.player_names: if arguments.player is not None and arguments.player != player.name: From 3c2fa1625d1fc0c7399219ef1ba2af771b5d0bf6 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 28 Nov 2021 01:12:35 +0300 Subject: [PATCH 60/66] Finish --- meson.build | 5 +++++ meson_options.txt | 1 + protocol/meson.build | 5 ++++- src/factory.cpp | 2 ++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 62ac8e3..52c9d29 100644 --- a/meson.build +++ b/meson.build @@ -257,6 +257,10 @@ else src_files += 'src/modules/simpleclock.cpp' endif +if get_option('experimental') + add_project_arguments('-DUSE_EXPERIMENTAL', language: 'cpp') +endif + subdir('protocol') executable( @@ -383,3 +387,4 @@ if clangtidy.found() '-p', meson.build_root() ] + src_files) endif + diff --git a/meson_options.txt b/meson_options.txt index 81e4468..f4f60d0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,3 +11,4 @@ option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk- 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') +option('experimental', type : 'boolean', value : false, description: 'Enable experimental features') diff --git a/protocol/meson.build b/protocol/meson.build index 6e82d63..8aed83d 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -27,11 +27,14 @@ 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'], - ['ext-workspace-unstable-v1.xml'], ['river-status-unstable-v1.xml'], ['river-control-unstable-v1.xml'], ] +if get_option('experimental') + client_protocols += ['ext-workspace-unstable-v1.xml'] +endif + client_protos_src = [] client_protos_headers = [] diff --git a/src/factory.cpp b/src/factory.cpp index a577751..351aa33 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -30,10 +30,12 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "wlr/taskbar") { return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]); } +#ifdef USE_EXPERIMENTAL if (ref == "wlr/workspaces") { return new waybar::modules::wlr::WorkspaceManager(id, bar_, config_[name]); } #endif +#endif #ifdef HAVE_RIVER if (ref == "river/tags") { return new waybar::modules::river::Tags(id, bar_, config_[name]); From 2fb671f5fa1aab8fc52aa0208b586cd0824cada2 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 28 Nov 2021 01:19:21 +0300 Subject: [PATCH 61/66] Revert protocol build --- protocol/meson.build | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/protocol/meson.build b/protocol/meson.build index 8aed83d..6e82d63 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -27,14 +27,11 @@ 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'], + ['ext-workspace-unstable-v1.xml'], ['river-status-unstable-v1.xml'], ['river-control-unstable-v1.xml'], ] -if get_option('experimental') - client_protocols += ['ext-workspace-unstable-v1.xml'] -endif - client_protos_src = [] client_protos_headers = [] From 4b5dc1bb3a5cda181bc0589992fb13076e3ad2b3 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 28 Nov 2021 09:52:18 -0800 Subject: [PATCH 62/66] test: count copies and moves done by SafeSignal --- test/GlibTestsFixture.hpp | 5 +++ test/SafeSignal.cpp | 84 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/test/GlibTestsFixture.hpp b/test/GlibTestsFixture.hpp index ffe6fd3..a21c8e0 100644 --- a/test/GlibTestsFixture.hpp +++ b/test/GlibTestsFixture.hpp @@ -7,6 +7,11 @@ class GlibTestsFixture : public sigc::trackable { public: GlibTestsFixture() : main_loop_{Glib::MainLoop::create()} {} + void setTimeout(int timeout) { + Glib::signal_timeout().connect_once([]() { throw std::runtime_error("Test timed out"); }, + timeout); + } + void run(std::function fn) { Glib::signal_idle().connect_once(fn); main_loop_->run(); diff --git a/test/SafeSignal.cpp b/test/SafeSignal.cpp index b07e9ca..2c67317 100644 --- a/test/SafeSignal.cpp +++ b/test/SafeSignal.cpp @@ -5,10 +5,15 @@ #include #include +#include #include "GlibTestsFixture.hpp" using namespace waybar; + +template +using remove_cvref_t = typename std::remove_cv::type>::type; + /** * Basic sanity test for SafeSignal: * check that type deduction works, events are delivered and the order is right @@ -25,7 +30,7 @@ TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal basic functionality", "[signal][t std::thread producer; // timeout the test in 500ms - Glib::signal_timeout().connect_once([]() { throw std::runtime_error("Test timed out"); }, 500); + setTimeout(500); test_signal.connect([&](auto val, auto str) { static_assert(std::is_same::value); @@ -57,6 +62,83 @@ TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal basic functionality", "[signal][t REQUIRE(count == NUM_EVENTS); } +template +struct TestObject { + T value; + unsigned copied = 0; + unsigned moved = 0; + + TestObject(const T& v) : value(v){}; + ~TestObject() = default; + + TestObject(const TestObject& other) + : value(other.value), copied(other.copied + 1), moved(other.moved) {} + + TestObject(TestObject&& other) noexcept + : value(std::move(other.value)), + copied(std::exchange(other.copied, 0)), + moved(std::exchange(other.moved, 0) + 1) {} + + TestObject& operator=(const TestObject& other) { + value = other.value; + copied = other.copied + 1; + moved = other.moved; + return *this; + } + + TestObject& operator=(TestObject&& other) noexcept { + value = std::move(other.value); + copied = std::exchange(other.copied, 0); + moved = std::exchange(other.moved, 0) + 1; + return *this; + } + + bool operator==(T other) const { return value == other; } + operator T() const { return value; } +}; + +/* + * Check the number of copies/moves performed on the object passed through SafeSignal + */ +TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal copy/move counter", "[signal][thread][util]") { + const int NUM_EVENTS = 3; + int count = 0; + + SafeSignal> test_signal; + + std::thread producer; + + // timeout the test in 500ms + setTimeout(500); + + test_signal.connect([&](auto& val) { + static_assert(std::is_same, remove_cvref_t>::value); + + /* explicit move in the producer thread */ + REQUIRE(val.moved <= 1); + /* copy within the SafeSignal queuing code */ + REQUIRE(val.copied <= 1); + + if (++count >= NUM_EVENTS) { + this->quit(); + }; + }); + + run([&]() { + test_signal.emit(1); + REQUIRE(count == 1); + producer = std::thread([&]() { + for (auto i = 2; i <= NUM_EVENTS; ++i) { + TestObject t{i}; + // check that signal.emit accepts moved objects + test_signal.emit(std::move(t)); + } + }); + }); + producer.join(); + REQUIRE(count == NUM_EVENTS); +} + int main(int argc, char* argv[]) { Glib::init(); return Catch::Session().run(argc, argv); From cf5ddb2a5e25121cb176c1566e89184c810092e3 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 28 Nov 2021 11:34:21 -0800 Subject: [PATCH 63/66] fix(swaybar-ipc): avoid unnecessary copy of struct swaybar_config --- src/modules/sway/bar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sway/bar.cpp b/src/modules/sway/bar.cpp index 78e524a..6ad74af 100644 --- a/src/modules/sway/bar.cpp +++ b/src/modules/sway/bar.cpp @@ -71,7 +71,7 @@ void BarIpcClient::onIpcEvent(const struct Ipc::ipc_response& res) { } else { // configuration update auto config = parseConfig(payload); - signal_config_(config); + signal_config_(std::move(config)); } } catch (const std::exception& e) { spdlog::error("BarIpcClient::onEvent {}", e.what()); From b6d0a4b63fb298fe4df24a7679b683b65d64b727 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 28 Nov 2021 12:19:45 -0800 Subject: [PATCH 64/66] feat(bar): allow customization of bar modes Allow changing existing modes and adding new ones via `modes` configuration key. `modes` accepts a JSON object roughly described by the following type ```typescript type BarMode = { layer: 'bottom' | 'top' | 'overlay'; exclusive: bool; passthrough: bool; visible: bool; }; type BarModeList = { [name: string]: BarMode; }; ``` and will be merged with the default modes defined in `bar.cpp`. Note that with absence of other ways to set mode, only those defined in the `sway-bar(5)`[1] documentation could be used right now. [1]: https://github.com/swaywm/sway/blob/master/sway/sway-bar.5.scd --- src/bar.cpp | 62 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index 26d64ec..789bea9 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -63,6 +63,48 @@ const std::string_view Bar::MODE_DEFAULT = "default"; const std::string_view Bar::MODE_INVISIBLE = "invisible"; const std::string_view DEFAULT_BAR_ID = "bar-0"; +/* Deserializer for enum bar_layer */ +void from_json(const Json::Value& j, bar_layer& l) { + if (j == "bottom") { + l = bar_layer::BOTTOM; + } else if (j == "top") { + l = bar_layer::TOP; + } else if (j == "overlay") { + l = bar_layer::OVERLAY; + } +} + +/* Deserializer for struct bar_mode */ +void from_json(const Json::Value& j, bar_mode& m) { + if (j.isObject()) { + if (auto v = j["layer"]; v.isString()) { + from_json(v, m.layer); + } + if (auto v = j["exclusive"]; v.isBool()) { + m.exclusive = v.asBool(); + } + if (auto v = j["passthrough"]; v.isBool()) { + m.passthrough = v.asBool(); + } + if (auto v = j["visible"]; v.isBool()) { + m.visible = v.asBool(); + } + } +} + +/* Deserializer for JSON Object -> map + * Assumes that all the values in the object are deserializable to the same type. + */ +template ::value>> +void from_json(const Json::Value& j, std::map& m) { + if (j.isObject()) { + for (auto it = j.begin(); it != j.end(); ++it) { + from_json(*it, m[it.key().asString()]); + } + } +} + #ifdef HAVE_GTK_LAYER_SHELL struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { GLSSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} { @@ -527,23 +569,15 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) surface_impl_->setPosition(position); surface_impl_->setSize(width, height); - /* Init "default" mode from globals */ - auto& default_mode = configured_modes[MODE_DEFAULT]; - if (config["layer"] == "top") { - default_mode.layer = bar_layer::TOP; - } else if (config["layer"] == "overlay") { - default_mode.layer = bar_layer::OVERLAY; + /* Read custom modes if available */ + if (auto modes = config.get("modes", {}); modes.isObject()) { + from_json(modes, configured_modes); } - if (config["exclusive"].isBool()) { - default_mode.exclusive = config["exclusive"].asBool(); - } + /* Update "default" mode with the global bar options */ + from_json(config, configured_modes[MODE_DEFAULT]); - if (config["passthrough"].isBool()) { - default_mode.passthrough = config["passthrough"].asBool(); - } - - if (auto mode = config["mode"]; mode.isString()) { + if (auto mode = config.get("mode", {}); mode.isString()) { setMode(config["mode"].asString()); } else { setMode(MODE_DEFAULT); From 9dac851f6d0e63e4315c7a8447a4b956353a996b Mon Sep 17 00:00:00 2001 From: Patrick Nicolas Date: Tue, 30 Nov 2021 16:31:11 +0100 Subject: [PATCH 65/66] Allow sink in addition to source for pulse icon --- 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 cf42780..8f2b25d 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_source_name_}; + std::vector res = {current_sink_name_, 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 f573e32d0b788867a783e9b013b94a4f3029d83a Mon Sep 17 00:00:00 2001 From: John Fredriksson <94405030+jfred9@users.noreply.github.com> Date: Fri, 3 Dec 2021 23:56:51 +0100 Subject: [PATCH 66/66] bar: Fix crash when unplugging HDMI There is a double delete situation which causes a SIGSEGV to happen during destruction of bar. This was introduced by the group feature patch. The same object pointer is stored in two different vectors of unique_ptr element. Replace with shared_ptr to handle reference counting correctly and avoid double delete. --- include/bar.hpp | 8 ++++---- src/bar.cpp | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 4aa17c1..01a9d03 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -112,14 +112,14 @@ class Bar { Gtk::Box center_; Gtk::Box right_; Gtk::Box box_; - std::vector> modules_left_; - std::vector> modules_center_; - std::vector> modules_right_; + std::vector> modules_left_; + std::vector> modules_center_; + std::vector> modules_right_; #ifdef HAVE_SWAY using BarIpcClient = modules::sway::BarIpcClient; std::unique_ptr _ipc_client; #endif - std::vector> modules_all_; + std::vector> modules_all_; }; } // namespace waybar diff --git a/src/bar.cpp b/src/bar.cpp index fbd4623..133c29a 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -735,18 +735,19 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, Gtk module = factory.makeModule(ref); } - modules_all_.emplace_back(module); + std::shared_ptr module_sp(module); + modules_all_.emplace_back(module_sp); if (group) { group->pack_start(*module, false, false); } else { if (pos == "modules-left") { - modules_left_.emplace_back(module); + modules_left_.emplace_back(module_sp); } if (pos == "modules-center") { - modules_center_.emplace_back(module); + modules_center_.emplace_back(module_sp); } if (pos == "modules-right") { - modules_right_.emplace_back(module); + modules_right_.emplace_back(module_sp); } } module->dp.connect([module, &name] {