From 255c0ebe288f4ca3ab9812ed090e2d4c125c8cee Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 11 Apr 2025 08:00:35 +0200 Subject: [PATCH 1/4] wlr/workspaces: port to ext-workspace-v1 Rework implementation to match new protocol behavior. --- include/modules/wlr/workspace_manager.hpp | 224 +++--- .../modules/wlr/workspace_manager_binding.hpp | 10 +- man/waybar-wlr-workspaces.5.scd | 24 +- meson.build | 22 +- protocol/ext-workspace-unstable-v1.xml | 306 -------- protocol/meson.build | 7 +- src/modules/wlr/workspace_manager.cpp | 743 ++++++++---------- src/modules/wlr/workspace_manager_binding.cpp | 105 ++- 8 files changed, 531 insertions(+), 910 deletions(-) delete mode 100644 protocol/ext-workspace-unstable-v1.xml diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index f7cc759ec..7522134ee 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -5,168 +5,140 @@ #include #include -#include #include #include #include #include "AModule.hpp" #include "bar.hpp" -#include "ext-workspace-unstable-v1-client-protocol.h" +#include "ext-workspace-v1-client-protocol.h" namespace waybar::modules::wlr { -class WorkspaceManager; class WorkspaceGroup; +class Workspace; -class Workspace { +class WorkspaceManager final : public AModule { public: - Workspace(const waybar::Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, - zext_workspace_handle_v1 *workspace, uint32_t id, std::string name); - ~Workspace(); - auto update() -> void; - - 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); } - auto is_empty() const -> bool { return state_ & static_cast(State::EMPTY); } - auto is_persistent() const -> bool { return persistent_; } - // wlr stuff - 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 make_persistent() -> void; - auto handle_duplicate() -> void; - - auto handle_done() -> void; - auto handle_clicked(GdkEventButton *bt) -> bool; - 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), - EMPTY = (1 << 3), - }; + WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config); + ~WorkspaceManager() override; + void register_manager(wl_registry *registry, uint32_t name, uint32_t version); + void remove_workspace_group(uint32_t id); + void remove_workspace(uint32_t id); + void set_needs_sorting() { needs_sorting_ = true; } + + // wlr events + void handle_workspace_group(ext_workspace_group_handle_v1 *handle); + void handle_workspace(ext_workspace_handle_v1 *handle); + void handle_done(); + void handle_finished(); + + // wlr requests + void commit() const; private: - auto get_icon() -> std::string; + void update() override; + bool has_button(const Gtk::Button *button); + void sort_workspaces(); + void clear_buttons(); + void update_buttons(); - const Bar &bar_; - const Json::Value &config_; - WorkspaceGroup &workspace_group_; + static uint32_t group_global_id; + static uint32_t workspace_global_id; + uint32_t workspace_name = 0; - // wlr stuff - zext_workspace_handle_v1 *workspace_handle_; - uint32_t state_ = 0; + bool sort_by_id_ = false; + bool sort_by_name_ = true; + bool sort_by_coordinates_ = false; + bool active_only_ = false; + bool all_outputs_ = false; - uint32_t id_; - std::string name_; - std::vector coordinates_; - static std::map icons_map_; - std::string format_; - bool with_icon_ = false; - bool persistent_ = false; + const waybar::Bar &bar_; + Gtk::Box box_; - Gtk::Button button_; - Gtk::Box content_; - Gtk::Label label_; + ext_workspace_manager_v1 *ext_manager_ = nullptr; + std::vector> groups_; + std::vector> workspaces_; + + bool needs_sorting_ = false; }; class WorkspaceGroup { public: - WorkspaceGroup(const waybar::Bar &bar, Gtk::Box &box, const Json::Value &config, - WorkspaceManager &manager, zext_workspace_group_handle_v1 *workspace_group_handle, - uint32_t id); + WorkspaceGroup(WorkspaceManager &manager, ext_workspace_group_handle_v1 *handle, uint32_t id); ~WorkspaceGroup(); - auto update() -> void; - - 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 persistent_workspaces() -> std::vector & { return persistent_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; - auto fill_persistent_workspaces() -> void; - auto create_persistent_workspaces() -> 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 handle_done() -> void; - auto commit() -> void; - private: - static uint32_t workspace_global_id; - const waybar::Bar &bar_; - Gtk::Box &box_; - const Json::Value &config_; - WorkspaceManager &workspace_manager_; + u_int32_t id() const { return id_; } + bool has_output(const wl_output *output); + bool has_workspace(const ext_workspace_handle_v1 *workspace); - // wlr stuff - zext_workspace_group_handle_v1 *workspace_group_handle_; - wl_output *output_ = nullptr; + // wlr events + void handle_capabilities(uint32_t capabilities); + void handle_output_enter(wl_output *output); + void handle_output_leave(wl_output *output); + void handle_workspace_enter(ext_workspace_handle_v1 *handle); + void handle_workspace_leave(ext_workspace_handle_v1 *handle); + void handle_removed(); + private: + WorkspaceManager &workspaces_manager_; + ext_workspace_group_handle_v1 *ext_handle_; uint32_t id_; - std::vector> workspaces_; - bool need_to_sort = false; - std::vector persistent_workspaces_; - bool persistent_created_ = false; + std::vector outputs_; + std::vector workspaces_; }; -class WorkspaceManager : public AModule { +class Workspace { public: - WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config); - ~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; + Workspace(const Json::Value &config, WorkspaceManager &manager, ext_workspace_handle_v1 *handle, + uint32_t id, const std::string &name); + ~Workspace(); - // wlr stuff - auto register_manager(wl_registry *registry, uint32_t name, uint32_t version) -> void; - auto handle_workspace_group_create(zext_workspace_group_handle_v1 *workspace_group_handle) - -> void; - auto handle_done() -> void; - auto handle_finished() -> void; - auto commit() -> void; + ext_workspace_handle_v1 *handle() const { return ext_handle_; } + u_int32_t id() const { return id_; } + std::string &workspace_id() { return workspace_id_; } + std::string &name() { return name_; } + std::vector &coordinates() { return coordinates_; } + Gtk::Button &button() { return button_; } + bool is_active() const; + bool is_urgent() const; + bool is_hidden() const; + void update(); + + // wlr events + void handle_id(const std::string &id); + void handle_name(const std::string &name); + void handle_coordinates(const std::vector &coordinates); + void handle_state(uint32_t state); + void handle_capabilities(uint32_t capabilities); + void handle_removed(); + + // gdk events + bool handle_clicked(const GdkEventButton *button) const; private: - const waybar::Bar &bar_; - Gtk::Box box_; - std::vector> groups_; + std::string icon(); - // wlr stuff - zext_workspace_manager_v1 *workspace_manager_ = nullptr; + WorkspaceManager &workspace_manager_; + ext_workspace_handle_v1 *ext_handle_ = nullptr; + uint32_t id_; + uint32_t state_ = 0; + std::string workspace_id_; + std::string name_; + std::vector coordinates_; - static uint32_t group_global_id; + std::string format_; + bool with_icon_ = false; + static std::map icon_map_; + std::string on_click_action_; + std::string on_click_middle_action_; + std::string on_click_right_action_; - bool sort_by_name_ = true; - bool sort_by_coordinates_ = true; - bool sort_by_number_ = false; - bool all_outputs_ = false; - bool active_only_ = false; - bool creation_delayed_ = false; + Gtk::Button button_; + Gtk::Box content_; + Gtk::Label label_; + + bool needs_updating_ = false; }; } // namespace waybar::modules::wlr diff --git a/include/modules/wlr/workspace_manager_binding.hpp b/include/modules/wlr/workspace_manager_binding.hpp index cc242c946..a64d9b02b 100644 --- a/include/modules/wlr/workspace_manager_binding.hpp +++ b/include/modules/wlr/workspace_manager_binding.hpp @@ -1,10 +1,10 @@ -#include "ext-workspace-unstable-v1-client-protocol.h" +#include "ext-workspace-v1-client-protocol.h" namespace waybar::modules::wlr { void add_registry_listener(void *data); -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 add_workspace_listener(ext_workspace_handle_v1 *workspace_handle, void *data); +void add_workspace_group_listener(ext_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); +ext_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name, + uint32_t version, void *data); } // namespace waybar::modules::wlr diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-wlr-workspaces.5.scd index 62d3f6364..433ded15c 100644 --- a/man/waybar-wlr-workspaces.5.scd +++ b/man/waybar-wlr-workspaces.5.scd @@ -24,18 +24,18 @@ Addressed by *wlr/workspaces* *sort-by-name*: ++ typeof: bool ++ default: true ++ - Should workspaces be sorted by name. + Should workspaces be sorted by name. Workspace names will be sorted numerically when all names are numbers. *sort-by-coordinates*: ++ typeof: bool ++ - default: true ++ + 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. -*sort-by-number*: ++ +*sort-by-id*: ++ typeof: bool ++ default: false ++ - If set to true, workspace names will be sorted numerically. Takes precedence over any other sort-by option. + Should workspaces be sorted by ID. Workspace ID will be sorted numerically when all ID are numbers. Takes precedence over any other sort-by option. *all-outputs*: ++ typeof: bool ++ @@ -49,7 +49,9 @@ Addressed by *wlr/workspaces* # FORMAT REPLACEMENTS -*{name}*: Name of workspace assigned by compositor +*{name}*: Name of workspace assigned by compositor. + +*{id}*: ID of workspace assigned by compositor. *{icon}*: Icon, as defined in *format-icons*. @@ -71,16 +73,16 @@ In addition to workspace name matching, the following *format-icons* can be set. ``` "wlr/workspaces": { "format": "{name}: {icon}", + "on-click": "activate", "format-icons": { - "1": "", - "2": "", - "3": "", - "4": "", - "5": "", + "Workspace 1": "", + "Workspace 2": "", + "Workspace 3": "", + "Workspace 4": "", "active": "", "default": "" }, - "sort-by-number": true + "sort-by-id": true } ``` diff --git a/meson.build b/meson.build index 8832a55e4..caf6a06b5 100644 --- a/meson.build +++ b/meson.build @@ -276,6 +276,17 @@ if true man_files += files('man/waybar-wlr-taskbar.5.scd') endif +if wayland_protos.version().version_compare('>=1.39') + add_project_arguments('-DHAVE_WLR_WORKSPACES', language: 'cpp') + src_files += files( + 'src/modules/wlr/workspace_manager.cpp', + 'src/modules/wlr/workspace_manager_binding.cpp', + ) + man_files += files( + 'man/waybar-wlr-workspaces.5.scd', + ) +endif + if true add_project_arguments('-DHAVE_RIVER', language: 'cpp') src_files += files( @@ -485,17 +496,6 @@ else man_files += files('man/waybar-clock.5.scd') endif -if get_option('experimental') - add_project_arguments('-DHAVE_WLR_WORKSPACES', language: 'cpp') - src_files += files( - 'src/modules/wlr/workspace_manager.cpp', - 'src/modules/wlr/workspace_manager_binding.cpp', - ) - man_files += files( - 'man/waybar-wlr-workspaces.5.scd', - ) -endif - cava = dependency('cava', version : '>=0.10.4', required: get_option('cava'), diff --git a/protocol/ext-workspace-unstable-v1.xml b/protocol/ext-workspace-unstable-v1.xml deleted file mode 100644 index 24410b628..000000000 --- a/protocol/ext-workspace-unstable-v1.xml +++ /dev/null @@ -1,306 +0,0 @@ - - - - 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 zext_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 - zext_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 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 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 - zext_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 - 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. - - - - - - 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 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. - - - - - 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 - zext_workspace_handle_v1. - - - - - - - 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. - - - - - - 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 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 - the destruction of the object. - - - - - - - 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 - 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. - - 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 zext_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 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. - - 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 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. - - - - - - - The different states that a workspace can have. - - - - - - - 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 zext_workspace_handle_v1 has been destroyed. It is - guaranteed there won't be any more events for this - zext_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so - any requests will be ignored except the destroy request. - - - - - - 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 - 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. - - - - - - 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 cd9a77b14..b16113b2b 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -26,12 +26,17 @@ client_protocols = [ [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], ['wlr-foreign-toplevel-management-unstable-v1.xml'], - ['ext-workspace-unstable-v1.xml'], ['river-status-unstable-v1.xml'], ['river-control-unstable-v1.xml'], ['dwl-ipc-unstable-v2.xml'], ] +if wayland_protos.version().version_compare('>=1.39') + client_protocols += [ + [wl_protocol_dir, 'staging/ext-workspace/ext-workspace-v1.xml'] + ] +endif + client_protos_src = [] client_protos_headers = [] diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index f556a161e..9cc6d4efa 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -5,8 +5,7 @@ #include #include -#include -#include +#include #include #include "client.hpp" @@ -15,39 +14,52 @@ namespace waybar::modules::wlr { -uint32_t WorkspaceGroup::workspace_global_id = 0; +// WorkspaceManager + uint32_t WorkspaceManager::group_global_id = 0; -std::map Workspace::icons_map_; +uint32_t WorkspaceManager::workspace_global_id = 0; +std::map Workspace::icon_map_; WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config) : waybar::AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.orientation, 0) { - auto config_sort_by_name = config_["sort-by-name"]; + add_registry_listener(this); + + // parse configuration + + const auto config_sort_by_number = config_["sort-by-number"]; + if (config_sort_by_number.isBool()) { + spdlog::warn("[wlr/workspaces]: Prefer sort-by-id instead of sort-by-number"); + sort_by_id_ = config_sort_by_number.asBool(); + } + + const auto config_sort_by_id = config_["sort-by-id"]; + if (config_sort_by_id.isBool()) { + sort_by_id_ = config_sort_by_id.asBool(); + } + + const 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"]; + const 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_sort_by_number = config_["sort-by-number"]; - if (config_sort_by_number.isBool()) { - sort_by_number_ = config_sort_by_number.asBool(); - } - - auto config_all_outputs = config_["all-outputs"]; + const 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"]; + const auto config_active_only = config_["active-only"]; if (config_active_only.isBool()) { active_only_ = config_active_only.asBool(); - creation_delayed_ = active_only_; } + // setup UI + box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -55,352 +67,297 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); - add_registry_listener(this); - if (!workspace_manager_) { - return; - } + spdlog::debug("[wlr/workspaces]: Workspace manager created"); } -auto WorkspaceManager::workspace_comparator() const - -> std::function &, std::unique_ptr &)> { - return [=, this](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_number_) { - try { - auto is_number_less = std::stoi(lhs->get_name()) < std::stoi(rhs->get_name()); - return is_number_less; - } catch (const std::invalid_argument &) { - } - } +WorkspaceManager::~WorkspaceManager() { + workspaces_.clear(); + groups_.clear(); - if (sort_by_name_) { - if (sort_by_coordinates_) { - return is_name_eq ? is_coords_less : is_name_less; - } else { - return is_name_less; - } - } + if (ext_manager_ != nullptr) { + auto *display = Client::inst()->wl_display; + // Send `stop` request and wait for one roundtrip. + ext_workspace_manager_v1_stop(ext_manager_); + wl_display_roundtrip(display); + } - if (sort_by_coordinates_) { - return is_coords_less; - } + if (ext_manager_ != nullptr) { + spdlog::warn("[wlr/workspaces]: Destroying workspace manager before .finished event"); + ext_workspace_manager_v1_destroy(ext_manager_); + } - return lhs->id() < rhs->id(); - }; + spdlog::debug("[wlr/workspaces]: Workspace manager destroyed"); } -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; - } +void WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) { + if (ext_manager_ != nullptr) { + spdlog::warn("[wlr/workspaces]: Register workspace manager again although already registered!"); + return; + } + if (version != 1) { + spdlog::warn("[wlr/workspaces]: Using different workspace manager protocol version: {}", + version); + } - for (auto &workspace : group_workspaces) { - if (!workspace->is_active()) { - continue; - } + ext_manager_ = workspace_manager_bind(registry, name, version, this); +} - all_workspaces.push_back(workspace); - } - } +void WorkspaceManager::remove_workspace_group(uint32_t id) { + const auto it = + std::find_if(groups_.begin(), groups_.end(), [id](const auto &g) { return g->id() == id; }); - 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); + if (it == groups_.end()) { + spdlog::warn("[wlr/workspaces]: Can't find workspace group with id {}", id); + return; } + + groups_.erase(it); } -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!"); +void WorkspaceManager::remove_workspace(uint32_t id) { + const auto it = std::find_if(workspaces_.begin(), workspaces_.end(), + [id](const auto &w) { return w->id() == id; }); + + if (it == workspaces_.end()) { + spdlog::warn("[wlr/workspaces]: Can't find workspace with id {}", id); return; } - if (version != 1) { - spdlog::warn("Using different workspace manager protocol version: {}", version); - } - workspace_manager_ = workspace_manager_bind(registry, name, version, this); + + workspaces_.erase(it); } -auto WorkspaceManager::handle_workspace_group_create( - 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)); - spdlog::debug("Workspace group {} created", new_id); +void WorkspaceManager::handle_workspace_group(ext_workspace_group_handle_v1 *handle) { + const auto new_id = ++group_global_id; + groups_.push_back(std::make_unique(*this, handle, new_id)); + spdlog::debug("[wlr/workspaces]: Workspace group {} created", new_id); } -auto WorkspaceManager::handle_finished() -> void { - zext_workspace_manager_v1_destroy(workspace_manager_); - workspace_manager_ = nullptr; +void WorkspaceManager::handle_workspace(ext_workspace_handle_v1 *handle) { + const auto new_id = ++workspace_global_id; + const auto new_name = std::to_string(++workspace_name); + workspaces_.push_back(std::make_unique(config_, *this, handle, new_id, new_name)); + set_needs_sorting(); + spdlog::debug("[wlr/workspaces]: Workspace {} created", new_id); } -auto WorkspaceManager::handle_done() -> void { - for (auto &group : groups_) { - group->handle_done(); - } - dp.emit(); +void WorkspaceManager::handle_done() { dp.emit(); } + +void WorkspaceManager::handle_finished() { + spdlog::debug("[wlr/workspaces]: Finishing workspace manager"); + ext_workspace_manager_v1_destroy(ext_manager_); + ext_manager_ = nullptr; } -auto WorkspaceManager::update() -> void { - for (auto &group : groups_) { - group->update(); - } - if (creation_delayed()) { - creation_delayed_ = false; +void WorkspaceManager::commit() const { ext_workspace_manager_v1_commit(ext_manager_); } + +void WorkspaceManager::update() { + spdlog::debug("[wlr/workspaces]: Updating state"); + + if (needs_sorting_) { + clear_buttons(); sort_workspaces(); + needs_sorting_ = false; } + + update_buttons(); AModule::update(); } -WorkspaceManager::~WorkspaceManager() { - if (!workspace_manager_) { - return; - } +bool WorkspaceManager::has_button(const Gtk::Button *button) { + const auto buttons = box_.get_children(); + return std::find(buttons.begin(), buttons.end(), button) != buttons.end(); +} - wl_display *display = Client::inst()->wl_display; +void WorkspaceManager::sort_workspaces() { + // determine if workspace ID's and names can be sort numerically or literally - // Send `stop` request and wait for one roundtrip. This is not quite correct as - // the protocol encourages us to wait for the .finished event, but it should work - // with wlroots workspace manager implementation. - zext_workspace_manager_v1_stop(workspace_manager_); - wl_display_roundtrip(display); + auto is_numeric = [](const std::string &s) { + return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); + }; - // If the .finished handler is still not executed, destroy the workspace manager here. - if (workspace_manager_) { - spdlog::warn("Foreign toplevel manager destroyed before .finished event"); - zext_workspace_manager_v1_destroy(workspace_manager_); - workspace_manager_ = nullptr; - } -} + auto sort_by_workspace_id_numerically = + std::all_of(workspaces_.begin(), workspaces_.end(), + [&](const auto &w) { return is_numeric(w->workspace_id()); }); -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; }); + auto sort_by_name_numerically = std::all_of(workspaces_.begin(), workspaces_.end(), + [&](const auto &w) { return is_numeric(w->name()); }); - if (it == groups_.end()) { - spdlog::warn("Can't find group with id {}", id); - return; - } + // sort based on configuration setting with sort-by-id as fallback - groups_.erase(it); -} -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, - zext_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_(id) { - add_workspace_group_listener(workspace_group_handle, this); -} - -auto WorkspaceGroup::fill_persistent_workspaces() -> void { - if (config_["persistent_workspaces"].isObject()) { - spdlog::warn( - "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); - } - - if ((config_["persistent-workspaces"].isObject() || - config_["persistent_workspaces"].isObject()) && - !workspace_manager_.all_outputs()) { - const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject() - ? config_["persistent-workspaces"] - : config_["persistent_workspaces"]; - const std::vector p_workspaces_names = p_workspaces.getMemberNames(); - - for (const std::string &p_w_name : p_workspaces_names) { - const Json::Value &p_w = p_workspaces[p_w_name]; - if (p_w.isArray() && !p_w.empty()) { - // Adding to target outputs - for (const Json::Value &output : p_w) { - if (output.asString() == bar_.output->name) { - persistent_workspaces_.push_back(p_w_name); - break; - } - } - } else { - // Adding to all outputs - persistent_workspaces_.push_back(p_w_name); + std::sort(workspaces_.begin(), workspaces_.end(), [&](const auto &w1, const auto &w2) { + if (sort_by_id_ || (!sort_by_name_ && !sort_by_coordinates_)) { + if (w1->workspace_id() == w2->workspace_id()) { + return w1->id() < w2->id(); + } + if (sort_by_workspace_id_numerically) { + // the idea is that phonetic compare can be applied just to numbers + // with same number of digits + return w1->workspace_id().size() < w2->workspace_id().size() || + (w1->workspace_id().size() == w2->workspace_id().size() && + w1->workspace_id() < w2->workspace_id()); } + return w1->workspace_id() < w2->workspace_id(); } - } -} -auto WorkspaceGroup::create_persistent_workspaces() -> void { - for (const std::string &p_w_name : persistent_workspaces_) { - auto new_id = ++workspace_global_id; - workspaces_.push_back( - std::make_unique(bar_, config_, *this, nullptr, new_id, p_w_name)); - spdlog::debug("Workspace {} created", new_id); - } -} + if (sort_by_name_) { + if (w1->name() == w2->name()) { + return w1->id() < w2->id(); + } + if (sort_by_name_numerically) { + // see above about numeric sorting + return w1->name().size() < w2->name().size() || + (w1->name().size() == w2->name().size() && w1->name() < w2->name()); + } + return w1->name() < w2->name(); + } -auto WorkspaceGroup::active_only() const -> bool { return workspace_manager_.active_only(); } -auto WorkspaceGroup::creation_delayed() const -> bool { - return workspace_manager_.creation_delayed(); -} + if (sort_by_coordinates_) { + if (w1->coordinates() == w2->coordinates()) { + return w1->id() < w2->id(); + } + return w1->coordinates() < w2->coordinates(); + } -auto WorkspaceGroup::add_button(Gtk::Button &button) -> void { - box_.pack_start(button, false, false); + return w1->id() < w2->id(); + }); } -WorkspaceGroup::~WorkspaceGroup() { - if (!workspace_group_handle_) { - return; +void WorkspaceManager::clear_buttons() { + for (const auto &workspace : workspaces_) { + if (has_button(&workspace->button())) { + box_.remove(workspace->button()); + } } - - zext_workspace_group_handle_v1_destroy(workspace_group_handle_); - workspace_group_handle_ = nullptr; } -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); - if (!persistent_created_) { - fill_persistent_workspaces(); - create_persistent_workspaces(); - persistent_created_ = true; - } -} +void WorkspaceManager::update_buttons() { + const auto *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); -auto WorkspaceGroup::handle_remove() -> void { - zext_workspace_group_handle_v1_destroy(workspace_group_handle_); - workspace_group_handle_ = nullptr; - workspace_manager_.remove_workspace_group(id_); -} + // go through all workspace -auto WorkspaceGroup::handle_output_enter(wl_output *output) -> void { - spdlog::debug("Output {} assigned to {} group", (void *)output, id_); - output_ = output; + for (const auto &workspace : workspaces_) { + const bool workspace_on_any_group_for_output = + std::any_of(groups_.begin(), groups_.end(), [&](const auto &group) { + const bool group_on_output = group->has_output(output) || all_outputs_; + const bool workspace_on_group = group->has_workspace(workspace->handle()); + return group_on_output && workspace_on_group; + }); + const bool active_or_urgent = workspace->is_active() || workspace->is_urgent(); + const bool valid_workspace_state = (active_or_urgent && active_only_) || !active_only_; - if (!is_visible() || workspace_manager_.creation_delayed()) { - return; - } + const bool show_button = workspace_on_any_group_for_output && valid_workspace_state; + const bool bar_contains_button = has_button(&workspace->button()); - for (auto &workspace : workspaces_) { - add_button(workspace->get_button_ref()); - } -} + // add or remove buttons if needed, update button state -auto WorkspaceGroup::is_visible() const -> bool { - return output_ != nullptr && - (workspace_manager_.all_outputs() || - output_ == gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj())); + if (show_button) { + if (!bar_contains_button) { + // add button to bar + box_.pack_start(workspace->button(), false, false); + workspace->button().show_all(); + } + workspace->update(); + } else { + if (bar_contains_button) { + // remove button from bar + box_.remove(workspace->button()); + } + } + } } -auto WorkspaceGroup::handle_output_leave() -> void { - spdlog::debug("Output {} remove from {} group", (void *)output_, id_); - output_ = nullptr; +// WorkspaceGroup - if (output_ != gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj())) { - return; - } +WorkspaceGroup::WorkspaceGroup(WorkspaceManager &manager, ext_workspace_group_handle_v1 *handle, + uint32_t id) + : workspaces_manager_(manager), ext_handle_(handle), id_(id) { + add_workspace_group_listener(ext_handle_, this); +} - for (auto &workspace : workspaces_) { - remove_button(workspace->get_button_ref()); +WorkspaceGroup::~WorkspaceGroup() { + if (ext_handle_ != nullptr) { + ext_workspace_group_handle_v1_destroy(ext_handle_); } + spdlog::debug("[wlr/workspaces]: Workspace group {} destroyed", id_); } -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(); - } +bool WorkspaceGroup::has_output(const wl_output *output) { + return std::find(outputs_.begin(), outputs_.end(), output) != outputs_.end(); } -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; }); +bool WorkspaceGroup::has_workspace(const ext_workspace_handle_v1 *workspace) { + return std::find(workspaces_.begin(), workspaces_.end(), workspace) != workspaces_.end(); +} - if (it == workspaces_.end()) { - spdlog::warn("Can't find workspace with id {}", id); - return; +void WorkspaceGroup::handle_capabilities(uint32_t capabilities) { + spdlog::debug("[wlr/workspaces]: Capabilities for workspace group {}:", id_); + if ((capabilities & EXT_WORKSPACE_GROUP_HANDLE_V1_GROUP_CAPABILITIES_CREATE_WORKSPACE) == + capabilities) { + spdlog::debug("[wlr/workspaces]: create-workspace"); } - - workspaces_.erase(it); } -auto WorkspaceGroup::handle_done() -> void { - need_to_sort = false; - if (!is_visible()) { - return; - } +void WorkspaceGroup::handle_output_enter(wl_output *output) { outputs_.push_back(output); } - for (auto &workspace : workspaces_) { - workspace->handle_done(); +void WorkspaceGroup::handle_output_leave(wl_output *output) { + const auto it = std::find(outputs_.begin(), outputs_.end(), output); + if (it != outputs_.end()) { + outputs_.erase(it); } +} - if (creation_delayed()) { - return; - } +void WorkspaceGroup::handle_workspace_enter(ext_workspace_handle_v1 *handle) { + workspaces_.push_back(handle); +} - if (!workspace_manager_.all_outputs()) { - sort_workspaces(); - } else { - workspace_manager_.sort_workspaces(); +void WorkspaceGroup::handle_workspace_leave(ext_workspace_handle_v1 *handle) { + const auto it = std::find(workspaces_.begin(), workspaces_.end(), handle); + if (it != workspaces_.end()) { + workspaces_.erase(it); } } -auto WorkspaceGroup::commit() -> void { workspace_manager_.commit(); } - -auto WorkspaceGroup::sort_workspaces() -> void { - 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); - } +void WorkspaceGroup::handle_removed() { + spdlog::debug("[wlr/workspaces]: Removing workspace group {}", id_); + workspaces_manager_.remove_workspace_group(id_); } -auto WorkspaceGroup::remove_button(Gtk::Button &button) -> void { box_.remove(button); } +// Workspace -Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, - zext_workspace_handle_v1 *workspace, uint32_t id, std::string name) - : bar_(bar), - config_(config), - workspace_group_(workspace_group), - workspace_handle_(workspace), - id_(id), - name_(name) { - if (workspace) { - add_workspace_listener(workspace, this); - } else { - state_ = (uint32_t)State::EMPTY; - } +Workspace::Workspace(const Json::Value &config, WorkspaceManager &manager, + ext_workspace_handle_v1 *handle, uint32_t id, const std::string &name) + : workspace_manager_(manager), ext_handle_(handle), id_(id), workspace_id_(name), name_(name) { + add_workspace_listener(ext_handle_, this); - auto config_format = config["format"]; + // parse configuration + const 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()); + if (with_icon_ && icon_map_.empty()) { + const auto &format_icons = config["format-icons"]; + for (auto &n : format_icons.getMemberNames()) { + icon_map_.emplace(n, format_icons[n].asString()); } } - /* Handle click events if configured */ - if (config_["on-click"].isString() || config_["on-click-middle"].isString() || - config_["on-click-right"].isString()) { + const bool config_on_click = config["on-click"].isString(); + if (config_on_click) { + on_click_action_ = config["on-click"].asString(); + } + const bool config_on_click_middle = config["on-click-middle"].isString(); + if (config_on_click_middle) { + on_click_middle_action_ = config["on-click"].asString(); + } + const bool config_on_click_right = config["on-click-right"].isString(); + if (config_on_click_right) { + on_click_right_action_ = config["on-click"].asString(); + } + + // setup UI + + if (config_on_click || config_on_click_middle || config_on_click_right) { button_.add_events(Gdk::BUTTON_PRESS_MASK); button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked), false); @@ -409,177 +366,145 @@ 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; +Workspace::~Workspace() { + if (ext_handle_ != nullptr) { + ext_workspace_handle_v1_destroy(ext_handle_); } + spdlog::debug("[wlr/workspaces]: Workspace {} destroyed", id_); +} - workspace_group.add_button(button_); - button_.show_all(); +bool Workspace::is_active() const { + return (state_ & EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE) == EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE; } -Workspace::~Workspace() { - workspace_group_.remove_button(button_); - if (!workspace_handle_) { - return; - } +bool Workspace::is_urgent() const { + return (state_ & EXT_WORKSPACE_HANDLE_V1_STATE_URGENT) == EXT_WORKSPACE_HANDLE_V1_STATE_URGENT; +} - zext_workspace_handle_v1_destroy(workspace_handle_); - workspace_handle_ = nullptr; +bool Workspace::is_hidden() const { + return (state_ & EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN) == EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN; } -auto Workspace::update() -> void { - label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("name", name_), - fmt::arg("icon", with_icon_ ? get_icon() : ""))); -} - -auto Workspace::handle_state(const std::vector &state) -> void { - state_ = 0; - for (auto state_entry : state) { - switch (state_entry) { - 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::HIDDEN; - break; - } +void Workspace::update() { + if (!needs_updating_) { + return; } -} -auto Workspace::handle_remove() -> void { - if (workspace_handle_) { - zext_workspace_handle_v1_destroy(workspace_handle_); - workspace_handle_ = nullptr; + // update style + const auto style_context = button_.get_style_context(); + style_context->remove_class("active"); + style_context->remove_class("urgent"); + style_context->remove_class("hidden"); + + if (is_active()) { + style_context->add_class("active"); } - if (!persistent_) { - workspace_group_.remove_workspace(id_); - } else { - state_ = (uint32_t)State::EMPTY; + if (is_urgent()) { + style_context->add_class("urgent"); + } + if (is_hidden()) { + style_context->add_class("hidden"); } + + // update label + label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("name", name_), + fmt::arg("id", workspace_id_), + fmt::arg("icon", with_icon_ ? icon() : ""))); + + needs_updating_ = false; } -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); - } +void Workspace::handle_id(const std::string &id) { + workspace_id_ = id; + needs_updating_ = true; + workspace_manager_.set_needs_sorting(); } -auto Workspace::handle_done() -> void { - spdlog::debug("Workspace {} changed to state {}", id_, state_); - auto style_context = button_.get_style_context(); - 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"); - add_or_remove_class(style_context, is_empty(), "persistent"); +void Workspace::handle_name(const std::string &name) { + name_ = name; + needs_updating_ = true; + workspace_manager_.set_needs_sorting(); +} - if (workspace_group_.creation_delayed()) { - return; - } +void Workspace::handle_coordinates(const std::vector &coordinates) { + coordinates_ = coordinates; + needs_updating_ = true; + workspace_manager_.set_needs_sorting(); +} - if (workspace_group_.active_only() && (is_active() || is_urgent())) { - button_.show_all(); - } else if (workspace_group_.active_only() && !(is_active() || is_urgent())) { - button_.hide(); - } +void Workspace::handle_state(uint32_t state) { + state_ = state; + needs_updating_ = true; } -auto Workspace::get_icon() -> std::string { - if (is_active()) { - auto active_icon_it = icons_map_.find("active"); - if (active_icon_it != icons_map_.end()) { - return active_icon_it->second; - } +void Workspace::handle_capabilities(uint32_t capabilities) { + spdlog::debug("[wlr/workspaces]: Capabilities for workspace {}:", id_); + if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE) == capabilities) { + spdlog::debug("[wlr/workspaces]: activate"); } - - auto named_icon_it = icons_map_.find(name_); - if (named_icon_it != icons_map_.end()) { - return named_icon_it->second; + if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE) == capabilities) { + spdlog::debug("[wlr/workspaces]: deactivate"); } - - if (is_empty()) { - auto persistent_icon_it = icons_map_.find("persistent"); - if (persistent_icon_it != icons_map_.end()) { - return persistent_icon_it->second; - } + if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_REMOVE) == capabilities) { + spdlog::debug("[wlr/workspaces]: remove"); } - - auto default_icon_it = icons_map_.find("default"); - if (default_icon_it != icons_map_.end()) { - return default_icon_it->second; + if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ASSIGN) == capabilities) { + spdlog::debug("[wlr/workspaces]: assign"); } + needs_updating_ = true; +} - return name_; +void Workspace::handle_removed() { + spdlog::debug("[wlr/workspaces]: Removing workspace {}", id_); + workspace_manager_.remove_workspace(id_); } -auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { +bool Workspace::handle_clicked(const GdkEventButton *button) const { 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 (button->button == GDK_BUTTON_PRIMARY) { + action = on_click_action_; + } else if (button->button == GDK_BUTTON_MIDDLE) { + action = on_click_middle_action_; + } else if (button->button == GDK_BUTTON_SECONDARY) { + action = on_click_right_action_; } - if (action.empty()) + if (action.empty()) { return true; - else if (action == "activate") { - zext_workspace_handle_v1_activate(workspace_handle_); + } + + if (action == "activate") { + ext_workspace_handle_v1_activate(ext_handle_); } else if (action == "close") { - zext_workspace_handle_v1_remove(workspace_handle_); + ext_workspace_handle_v1_remove(ext_handle_); } else { - spdlog::warn("Unknown action {}", action); + spdlog::warn("[wlr/workspaces]: Unknown action {}", action); } - - workspace_group_.commit(); - + workspace_manager_.commit(); 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(); +std::string Workspace::icon() { + if (is_active()) { + const auto active_icon_it = icon_map_.find("active"); + if (active_icon_it != icon_map_.end()) { + return active_icon_it->second; + } } - name_ = name; - spdlog::debug("Workspace {} added to group {}", name, workspace_group_.id()); - make_persistent(); - handle_duplicate(); -} - -auto Workspace::make_persistent() -> void { - auto p_workspaces = workspace_group_.persistent_workspaces(); - - if (std::find(p_workspaces.begin(), p_workspaces.end(), name_) != p_workspaces.end()) { - persistent_ = true; + const auto named_icon_it = icon_map_.find(name_); + if (named_icon_it != icon_map_.end()) { + return named_icon_it->second; } -} -auto Workspace::handle_duplicate() -> void { - auto duplicate = - std::find_if(workspace_group_.workspaces().begin(), workspace_group_.workspaces().end(), - [this](const std::unique_ptr &g) { - return g->get_name() == name_ && g->id() != id_; - }); - if (duplicate != workspace_group_.workspaces().end()) { - workspace_group_.remove_workspace(duplicate->get()->id()); + const auto default_icon_it = icon_map_.find("default"); + if (default_icon_it != icon_map_.end()) { + return default_icon_it->second; } -} -auto Workspace::handle_coordinates(const std::vector &coordinates) -> void { - if (coordinates_ != coordinates) { - workspace_group_.set_need_to_sort(); - } - coordinates_ = coordinates; + return name_; } + } // namespace waybar::modules::wlr diff --git a/src/modules/wlr/workspace_manager_binding.cpp b/src/modules/wlr/workspace_manager_binding.cpp index 22e68fbf9..1333e607c 100644 --- a/src/modules/wlr/workspace_manager_binding.cpp +++ b/src/modules/wlr/workspace_manager_binding.cpp @@ -11,7 +11,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, zext_workspace_manager_v1_interface.name) == 0) { + if (std::strcmp(interface, ext_workspace_manager_v1_interface.name) == 0) { static_cast(data)->register_manager(registry, name, version); } } @@ -29,76 +29,97 @@ void add_registry_listener(void *data) { 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, zext_workspace_manager_v1 *_, zext_workspace_group_handle_v1 *workspace_group) { - static_cast(data)->handle_workspace_group_create(workspace_group); + void *data, ext_workspace_manager_v1 *_, ext_workspace_group_handle_v1 *workspace_group) { + static_cast(data)->handle_workspace_group(workspace_group); +} + +static void workspace_manager_handle_workspace(void *data, ext_workspace_manager_v1 *_, + ext_workspace_handle_v1 *workspace) { + static_cast(data)->handle_workspace(workspace); } -static void workspace_manager_handle_done(void *data, zext_workspace_manager_v1 *_) { +static void workspace_manager_handle_done(void *data, ext_workspace_manager_v1 *_) { static_cast(data)->handle_done(); } -static void workspace_manager_handle_finished(void *data, zext_workspace_manager_v1 *_) { +static void workspace_manager_handle_finished(void *data, ext_workspace_manager_v1 *_) { static_cast(data)->handle_finished(); } -static const zext_workspace_manager_v1_listener workspace_manager_impl = { +static const ext_workspace_manager_v1_listener workspace_manager_impl = { .workspace_group = workspace_manager_handle_workspace_group, + .workspace = workspace_manager_handle_workspace, .done = workspace_manager_handle_done, .finished = workspace_manager_handle_finished, }; -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, &zext_workspace_manager_v1_interface, version)); +ext_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, &ext_workspace_manager_v1_interface, version)); if (workspace_manager) - zext_workspace_manager_v1_add_listener(workspace_manager, &workspace_manager_impl, data); + ext_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, zext_workspace_group_handle_v1 *_, +static void workspace_group_handle_capabilities(void *data, ext_workspace_group_handle_v1 *_, + uint32_t capabilities) { + static_cast(data)->handle_capabilities(capabilities); +} + +static void workspace_group_handle_output_enter(void *data, ext_workspace_group_handle_v1 *_, wl_output *output) { static_cast(data)->handle_output_enter(output); } -static void workspace_group_handle_output_leave(void *data, zext_workspace_group_handle_v1 *_, +static void workspace_group_handle_output_leave(void *data, ext_workspace_group_handle_v1 *_, wl_output *output) { - static_cast(data)->handle_output_leave(); + static_cast(data)->handle_output_leave(output); } -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_workspace_enter(void *data, ext_workspace_group_handle_v1 *_, + ext_workspace_handle_v1 *workspace) { + static_cast(data)->handle_workspace_enter(workspace); } -static void workspace_group_handle_remove(void *data, zext_workspace_group_handle_v1 *_) { - static_cast(data)->handle_remove(); +static void workspace_group_handle_workspace_leave(void *data, ext_workspace_group_handle_v1 *_, + ext_workspace_handle_v1 *workspace) { + static_cast(data)->handle_workspace_leave(workspace); } -static const zext_workspace_group_handle_v1_listener workspace_group_impl = { +static void workspace_group_handle_removed(void *data, ext_workspace_group_handle_v1 *_) { + static_cast(data)->handle_removed(); +} + +static const ext_workspace_group_handle_v1_listener workspace_group_impl = { + .capabilities = workspace_group_handle_capabilities, .output_enter = workspace_group_handle_output_enter, .output_leave = workspace_group_handle_output_leave, - .workspace = workspace_group_handle_workspace, - .remove = workspace_group_handle_remove}; + .workspace_enter = workspace_group_handle_workspace_enter, + .workspace_leave = workspace_group_handle_workspace_leave, + .removed = workspace_group_handle_removed}; -void add_workspace_group_listener(zext_workspace_group_handle_v1 *workspace_group_handle, +void add_workspace_group_listener(ext_workspace_group_handle_v1 *workspace_group_handle, void *data) { - zext_workspace_group_handle_v1_add_listener(workspace_group_handle, &workspace_group_impl, data); + ext_workspace_group_handle_v1_add_listener(workspace_group_handle, &workspace_group_impl, data); } -void workspace_handle_name(void *data, struct zext_workspace_handle_v1 *_, const char *name) { +void workspace_handle_name(void *data, struct ext_workspace_handle_v1 *_, const char *name) { static_cast(data)->handle_name(name); } -void workspace_handle_coordinates(void *data, struct zext_workspace_handle_v1 *_, +void workspace_handle_id(void *data, struct ext_workspace_handle_v1 *_, const char *id) { + static_cast(data)->handle_id(id); +} + +void workspace_handle_coordinates(void *data, struct ext_workspace_handle_v1 *_, struct wl_array *coordinates) { std::vector coords_vec; auto coords = static_cast(coordinates->data); @@ -109,28 +130,30 @@ void workspace_handle_coordinates(void *data, struct zext_workspace_handle_v1 *_ static_cast(data)->handle_coordinates(coords_vec); } -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 / sizeof(uint32_t); ++i) { - state_vec.push_back(states[i]); - } +void workspace_handle_state(void *data, struct ext_workspace_handle_v1 *workspace_handle, + uint32_t state) { + static_cast(data)->handle_state(state); +} - static_cast(data)->handle_state(state_vec); +static void workspace_handle_capabilities(void *data, + struct ext_workspace_handle_v1 *workspace_handle, + uint32_t capabilities) { + static_cast(data)->handle_capabilities(capabilities); } -void workspace_handle_remove(void *data, struct zext_workspace_handle_v1 *_) { - static_cast(data)->handle_remove(); +void workspace_handle_removed(void *data, struct ext_workspace_handle_v1 *workspace_handle) { + static_cast(data)->handle_removed(); } -static const zext_workspace_handle_v1_listener workspace_impl = { +static const ext_workspace_handle_v1_listener workspace_impl = { + .id = workspace_handle_id, .name = workspace_handle_name, .coordinates = workspace_handle_coordinates, .state = workspace_handle_state, - .remove = workspace_handle_remove}; + .capabilities = workspace_handle_capabilities, + .removed = workspace_handle_removed}; -void add_workspace_listener(zext_workspace_handle_v1 *workspace_handle, void *data) { - zext_workspace_handle_v1_add_listener(workspace_handle, &workspace_impl, data); +void add_workspace_listener(ext_workspace_handle_v1 *workspace_handle, void *data) { + ext_workspace_handle_v1_add_listener(workspace_handle, &workspace_impl, data); } } // namespace waybar::modules::wlr From 29bf5d5da13fe1bcfbcf21e78aae36ba7abcfd2c Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 20 Jun 2025 07:17:46 +0200 Subject: [PATCH 2/4] ext/workspaces: moved/renamed from wlr/workspaces The ext-workspace-v1 protocol is not wlr/wlroots specific. --- .../{wlr => ext}/workspace_manager.hpp | 12 ++-- .../workspace_manager_binding.hpp | 4 +- ...aces.5.scd => waybar-ext-workspaces.5.scd} | 4 +- meson.build | 8 +-- src/factory.cpp | 10 ++-- .../{wlr => ext}/workspace_manager.cpp | 56 +++++++++---------- .../workspace_manager_binding.cpp | 8 +-- 7 files changed, 51 insertions(+), 51 deletions(-) rename include/modules/{wlr => ext}/workspace_manager.hpp (96%) rename include/modules/{wlr => ext}/workspace_manager_binding.hpp (87%) rename man/{waybar-wlr-workspaces.5.scd => waybar-ext-workspaces.5.scd} (97%) rename src/modules/{wlr => ext}/workspace_manager.cpp (89%) rename src/modules/{wlr => ext}/workspace_manager_binding.cpp (97%) diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/ext/workspace_manager.hpp similarity index 96% rename from include/modules/wlr/workspace_manager.hpp rename to include/modules/ext/workspace_manager.hpp index 7522134ee..747188485 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/ext/workspace_manager.hpp @@ -13,7 +13,7 @@ #include "bar.hpp" #include "ext-workspace-v1-client-protocol.h" -namespace waybar::modules::wlr { +namespace waybar::modules::ext { class WorkspaceGroup; class Workspace; @@ -27,13 +27,13 @@ class WorkspaceManager final : public AModule { void remove_workspace(uint32_t id); void set_needs_sorting() { needs_sorting_ = true; } - // wlr events + // wl events void handle_workspace_group(ext_workspace_group_handle_v1 *handle); void handle_workspace(ext_workspace_handle_v1 *handle); void handle_done(); void handle_finished(); - // wlr requests + // wl requests void commit() const; private: @@ -72,7 +72,7 @@ class WorkspaceGroup { bool has_output(const wl_output *output); bool has_workspace(const ext_workspace_handle_v1 *workspace); - // wlr events + // wl events void handle_capabilities(uint32_t capabilities); void handle_output_enter(wl_output *output); void handle_output_leave(wl_output *output); @@ -105,7 +105,7 @@ class Workspace { bool is_hidden() const; void update(); - // wlr events + // wl events void handle_id(const std::string &id); void handle_name(const std::string &name); void handle_coordinates(const std::vector &coordinates); @@ -141,4 +141,4 @@ class Workspace { bool needs_updating_ = false; }; -} // namespace waybar::modules::wlr +} // namespace waybar::modules::ext diff --git a/include/modules/wlr/workspace_manager_binding.hpp b/include/modules/ext/workspace_manager_binding.hpp similarity index 87% rename from include/modules/wlr/workspace_manager_binding.hpp rename to include/modules/ext/workspace_manager_binding.hpp index a64d9b02b..b41f207c7 100644 --- a/include/modules/wlr/workspace_manager_binding.hpp +++ b/include/modules/ext/workspace_manager_binding.hpp @@ -1,10 +1,10 @@ #include "ext-workspace-v1-client-protocol.h" -namespace waybar::modules::wlr { +namespace waybar::modules::ext { void add_registry_listener(void *data); void add_workspace_listener(ext_workspace_handle_v1 *workspace_handle, void *data); void add_workspace_group_listener(ext_workspace_group_handle_v1 *workspace_group_handle, void *data); ext_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name, uint32_t version, void *data); -} // namespace waybar::modules::wlr +} // namespace waybar::modules::ext diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-ext-workspaces.5.scd similarity index 97% rename from man/waybar-wlr-workspaces.5.scd rename to man/waybar-ext-workspaces.5.scd index 433ded15c..9a697cef9 100644 --- a/man/waybar-wlr-workspaces.5.scd +++ b/man/waybar-ext-workspaces.5.scd @@ -10,7 +10,7 @@ The *workspaces* module displays the currently used workspaces in wayland compos # CONFIGURATION -Addressed by *wlr/workspaces* +Addressed by *ext/workspaces* *format*: ++ typeof: string ++ @@ -71,7 +71,7 @@ In addition to workspace name matching, the following *format-icons* can be set. # EXAMPLES ``` -"wlr/workspaces": { +"ext/workspaces": { "format": "{name}: {icon}", "on-click": "activate", "format-icons": { diff --git a/meson.build b/meson.build index caf6a06b5..8d44dd674 100644 --- a/meson.build +++ b/meson.build @@ -277,13 +277,13 @@ if true endif if wayland_protos.version().version_compare('>=1.39') - add_project_arguments('-DHAVE_WLR_WORKSPACES', language: 'cpp') + add_project_arguments('-DHAVE_EXT_WORKSPACES', language: 'cpp') src_files += files( - 'src/modules/wlr/workspace_manager.cpp', - 'src/modules/wlr/workspace_manager_binding.cpp', + 'src/modules/ext/workspace_manager.cpp', + 'src/modules/ext/workspace_manager_binding.cpp', ) man_files += files( - 'man/waybar-wlr-workspaces.5.scd', + 'man/waybar-ext-workspaces.5.scd', ) endif diff --git a/src/factory.cpp b/src/factory.cpp index f7aa2d304..204081066 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -17,8 +17,8 @@ #ifdef HAVE_WLR_TASKBAR #include "modules/wlr/taskbar.hpp" #endif -#ifdef HAVE_WLR_WORKSPACES -#include "modules/wlr/workspace_manager.hpp" +#ifdef HAVE_EXT_WORKSPACES +#include "modules/ext/workspace_manager.hpp" #endif #ifdef HAVE_RIVER #include "modules/river/layout.hpp" @@ -178,9 +178,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]); } #endif -#ifdef HAVE_WLR_WORKSPACES - if (ref == "wlr/workspaces") { - return new waybar::modules::wlr::WorkspaceManager(id, bar_, config_[name]); +#ifdef HAVE_EXT_WORKSPACES + if (ref == "ext/workspaces") { + return new waybar::modules::ext::WorkspaceManager(id, bar_, config_[name]); } #endif #ifdef HAVE_RIVER diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/ext/workspace_manager.cpp similarity index 89% rename from src/modules/wlr/workspace_manager.cpp rename to src/modules/ext/workspace_manager.cpp index 9cc6d4efa..882aee71f 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/ext/workspace_manager.cpp @@ -1,4 +1,4 @@ -#include "modules/wlr/workspace_manager.hpp" +#include "modules/ext/workspace_manager.hpp" #include #include @@ -10,9 +10,9 @@ #include "client.hpp" #include "gtkmm/widget.h" -#include "modules/wlr/workspace_manager_binding.hpp" +#include "modules/ext/workspace_manager_binding.hpp" -namespace waybar::modules::wlr { +namespace waybar::modules::ext { // WorkspaceManager @@ -29,7 +29,7 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar const auto config_sort_by_number = config_["sort-by-number"]; if (config_sort_by_number.isBool()) { - spdlog::warn("[wlr/workspaces]: Prefer sort-by-id instead of sort-by-number"); + spdlog::warn("[ext/workspaces]: Prefer sort-by-id instead of sort-by-number"); sort_by_id_ = config_sort_by_number.asBool(); } @@ -67,7 +67,7 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); - spdlog::debug("[wlr/workspaces]: Workspace manager created"); + spdlog::debug("[ext/workspaces]: Workspace manager created"); } WorkspaceManager::~WorkspaceManager() { @@ -82,20 +82,20 @@ WorkspaceManager::~WorkspaceManager() { } if (ext_manager_ != nullptr) { - spdlog::warn("[wlr/workspaces]: Destroying workspace manager before .finished event"); + spdlog::warn("[ext/workspaces]: Destroying workspace manager before .finished event"); ext_workspace_manager_v1_destroy(ext_manager_); } - spdlog::debug("[wlr/workspaces]: Workspace manager destroyed"); + spdlog::debug("[ext/workspaces]: Workspace manager destroyed"); } void WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) { if (ext_manager_ != nullptr) { - spdlog::warn("[wlr/workspaces]: Register workspace manager again although already registered!"); + spdlog::warn("[ext/workspaces]: Register workspace manager again although already registered!"); return; } if (version != 1) { - spdlog::warn("[wlr/workspaces]: Using different workspace manager protocol version: {}", + spdlog::warn("[ext/workspaces]: Using different workspace manager protocol version: {}", version); } @@ -107,7 +107,7 @@ void WorkspaceManager::remove_workspace_group(uint32_t id) { std::find_if(groups_.begin(), groups_.end(), [id](const auto &g) { return g->id() == id; }); if (it == groups_.end()) { - spdlog::warn("[wlr/workspaces]: Can't find workspace group with id {}", id); + spdlog::warn("[ext/workspaces]: Can't find workspace group with id {}", id); return; } @@ -119,7 +119,7 @@ void WorkspaceManager::remove_workspace(uint32_t id) { [id](const auto &w) { return w->id() == id; }); if (it == workspaces_.end()) { - spdlog::warn("[wlr/workspaces]: Can't find workspace with id {}", id); + spdlog::warn("[ext/workspaces]: Can't find workspace with id {}", id); return; } @@ -129,7 +129,7 @@ void WorkspaceManager::remove_workspace(uint32_t id) { void WorkspaceManager::handle_workspace_group(ext_workspace_group_handle_v1 *handle) { const auto new_id = ++group_global_id; groups_.push_back(std::make_unique(*this, handle, new_id)); - spdlog::debug("[wlr/workspaces]: Workspace group {} created", new_id); + spdlog::debug("[ext/workspaces]: Workspace group {} created", new_id); } void WorkspaceManager::handle_workspace(ext_workspace_handle_v1 *handle) { @@ -137,13 +137,13 @@ void WorkspaceManager::handle_workspace(ext_workspace_handle_v1 *handle) { const auto new_name = std::to_string(++workspace_name); workspaces_.push_back(std::make_unique(config_, *this, handle, new_id, new_name)); set_needs_sorting(); - spdlog::debug("[wlr/workspaces]: Workspace {} created", new_id); + spdlog::debug("[ext/workspaces]: Workspace {} created", new_id); } void WorkspaceManager::handle_done() { dp.emit(); } void WorkspaceManager::handle_finished() { - spdlog::debug("[wlr/workspaces]: Finishing workspace manager"); + spdlog::debug("[ext/workspaces]: Finishing workspace manager"); ext_workspace_manager_v1_destroy(ext_manager_); ext_manager_ = nullptr; } @@ -151,7 +151,7 @@ void WorkspaceManager::handle_finished() { void WorkspaceManager::commit() const { ext_workspace_manager_v1_commit(ext_manager_); } void WorkspaceManager::update() { - spdlog::debug("[wlr/workspaces]: Updating state"); + spdlog::debug("[ext/workspaces]: Updating state"); if (needs_sorting_) { clear_buttons(); @@ -278,7 +278,7 @@ WorkspaceGroup::~WorkspaceGroup() { if (ext_handle_ != nullptr) { ext_workspace_group_handle_v1_destroy(ext_handle_); } - spdlog::debug("[wlr/workspaces]: Workspace group {} destroyed", id_); + spdlog::debug("[ext/workspaces]: Workspace group {} destroyed", id_); } bool WorkspaceGroup::has_output(const wl_output *output) { @@ -290,10 +290,10 @@ bool WorkspaceGroup::has_workspace(const ext_workspace_handle_v1 *workspace) { } void WorkspaceGroup::handle_capabilities(uint32_t capabilities) { - spdlog::debug("[wlr/workspaces]: Capabilities for workspace group {}:", id_); + spdlog::debug("[ext/workspaces]: Capabilities for workspace group {}:", id_); if ((capabilities & EXT_WORKSPACE_GROUP_HANDLE_V1_GROUP_CAPABILITIES_CREATE_WORKSPACE) == capabilities) { - spdlog::debug("[wlr/workspaces]: create-workspace"); + spdlog::debug("[ext/workspaces]: create-workspace"); } } @@ -318,7 +318,7 @@ void WorkspaceGroup::handle_workspace_leave(ext_workspace_handle_v1 *handle) { } void WorkspaceGroup::handle_removed() { - spdlog::debug("[wlr/workspaces]: Removing workspace group {}", id_); + spdlog::debug("[ext/workspaces]: Removing workspace group {}", id_); workspaces_manager_.remove_workspace_group(id_); } @@ -372,7 +372,7 @@ Workspace::~Workspace() { if (ext_handle_ != nullptr) { ext_workspace_handle_v1_destroy(ext_handle_); } - spdlog::debug("[wlr/workspaces]: Workspace {} destroyed", id_); + spdlog::debug("[ext/workspaces]: Workspace {} destroyed", id_); } bool Workspace::is_active() const { @@ -440,24 +440,24 @@ void Workspace::handle_state(uint32_t state) { } void Workspace::handle_capabilities(uint32_t capabilities) { - spdlog::debug("[wlr/workspaces]: Capabilities for workspace {}:", id_); + spdlog::debug("[ext/workspaces]: Capabilities for workspace {}:", id_); if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE) == capabilities) { - spdlog::debug("[wlr/workspaces]: activate"); + spdlog::debug("[ext/workspaces]: activate"); } if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE) == capabilities) { - spdlog::debug("[wlr/workspaces]: deactivate"); + spdlog::debug("[ext/workspaces]: deactivate"); } if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_REMOVE) == capabilities) { - spdlog::debug("[wlr/workspaces]: remove"); + spdlog::debug("[ext/workspaces]: remove"); } if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ASSIGN) == capabilities) { - spdlog::debug("[wlr/workspaces]: assign"); + spdlog::debug("[ext/workspaces]: assign"); } needs_updating_ = true; } void Workspace::handle_removed() { - spdlog::debug("[wlr/workspaces]: Removing workspace {}", id_); + spdlog::debug("[ext/workspaces]: Removing workspace {}", id_); workspace_manager_.remove_workspace(id_); } @@ -480,7 +480,7 @@ bool Workspace::handle_clicked(const GdkEventButton *button) const { } else if (action == "close") { ext_workspace_handle_v1_remove(ext_handle_); } else { - spdlog::warn("[wlr/workspaces]: Unknown action {}", action); + spdlog::warn("[ext/workspaces]: Unknown action {}", action); } workspace_manager_.commit(); return true; @@ -507,4 +507,4 @@ std::string Workspace::icon() { return name_; } -} // namespace waybar::modules::wlr +} // namespace waybar::modules::ext diff --git a/src/modules/wlr/workspace_manager_binding.cpp b/src/modules/ext/workspace_manager_binding.cpp similarity index 97% rename from src/modules/wlr/workspace_manager_binding.cpp rename to src/modules/ext/workspace_manager_binding.cpp index 1333e607c..2d9c6b48f 100644 --- a/src/modules/wlr/workspace_manager_binding.cpp +++ b/src/modules/ext/workspace_manager_binding.cpp @@ -1,13 +1,13 @@ -#include "modules/wlr/workspace_manager_binding.hpp" +#include "modules/ext/workspace_manager_binding.hpp" #include #include #include "client.hpp" -#include "modules/wlr/workspace_manager.hpp" +#include "modules/ext/workspace_manager.hpp" -namespace waybar::modules::wlr { +namespace waybar::modules::ext { static void handle_global(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { @@ -156,4 +156,4 @@ static const ext_workspace_handle_v1_listener workspace_impl = { void add_workspace_listener(ext_workspace_handle_v1 *workspace_handle, void *data) { ext_workspace_handle_v1_add_listener(workspace_handle, &workspace_impl, data); } -} // namespace waybar::modules::wlr +} // namespace waybar::modules::ext From 984d0de1fc7f92ddd498852be3d546f13cd704c0 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 20 Jun 2025 09:50:43 +0200 Subject: [PATCH 3/4] ext/workspaces: introduce ignore-hidden option Hide hidden workspaces by default, but add an option for overriding. While at it, move button visibility handling to Workspaces and only handle add/removal of buttons at WorkspaceManager. This makes it easier to keep track if sorting is needed. --- include/modules/ext/workspace_manager.hpp | 7 ++-- man/waybar-ext-workspaces.5.scd | 5 +++ src/modules/ext/workspace_manager.cpp | 50 +++++++++++------------ 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/include/modules/ext/workspace_manager.hpp b/include/modules/ext/workspace_manager.hpp index 747188485..0607b5ba7 100644 --- a/include/modules/ext/workspace_manager.hpp +++ b/include/modules/ext/workspace_manager.hpp @@ -50,7 +50,6 @@ class WorkspaceManager final : public AModule { bool sort_by_id_ = false; bool sort_by_name_ = true; bool sort_by_coordinates_ = false; - bool active_only_ = false; bool all_outputs_ = false; const waybar::Bar &bar_; @@ -100,9 +99,6 @@ class Workspace { std::string &name() { return name_; } std::vector &coordinates() { return coordinates_; } Gtk::Button &button() { return button_; } - bool is_active() const; - bool is_urgent() const; - bool is_hidden() const; void update(); // wl events @@ -117,6 +113,7 @@ class Workspace { bool handle_clicked(const GdkEventButton *button) const; private: + bool has_state(uint32_t state) const { return (state_ & state) == state; } std::string icon(); WorkspaceManager &workspace_manager_; @@ -127,6 +124,8 @@ class Workspace { std::string name_; std::vector coordinates_; + bool active_only_ = false; + bool ignore_hidden_ = true; std::string format_; bool with_icon_ = false; static std::map icon_map_; diff --git a/man/waybar-ext-workspaces.5.scd b/man/waybar-ext-workspaces.5.scd index 9a697cef9..54c67be21 100644 --- a/man/waybar-ext-workspaces.5.scd +++ b/man/waybar-ext-workspaces.5.scd @@ -47,6 +47,11 @@ Addressed by *ext/workspaces* default: false ++ If set to true only active or urgent workspaces will be shown. +*ignore-hidden*: ++ + typeof: bool ++ + default: true ++ + If set to false hidden workspaces will be shown. + # FORMAT REPLACEMENTS *{name}*: Name of workspace assigned by compositor. diff --git a/src/modules/ext/workspace_manager.cpp b/src/modules/ext/workspace_manager.cpp index 882aee71f..54750bb29 100644 --- a/src/modules/ext/workspace_manager.cpp +++ b/src/modules/ext/workspace_manager.cpp @@ -53,11 +53,6 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar all_outputs_ = config_all_outputs.asBool(); } - const auto config_active_only = config_["active-only"]; - if (config_active_only.isBool()) { - active_only_ = config_active_only.asBool(); - } - // setup UI box_.set_name("workspaces"); @@ -242,15 +237,11 @@ void WorkspaceManager::update_buttons() { const bool workspace_on_group = group->has_workspace(workspace->handle()); return group_on_output && workspace_on_group; }); - const bool active_or_urgent = workspace->is_active() || workspace->is_urgent(); - const bool valid_workspace_state = (active_or_urgent && active_only_) || !active_only_; - - const bool show_button = workspace_on_any_group_for_output && valid_workspace_state; const bool bar_contains_button = has_button(&workspace->button()); // add or remove buttons if needed, update button state - if (show_button) { + if (workspace_on_any_group_for_output) { if (!bar_contains_button) { // add button to bar box_.pack_start(workspace->button(), false, false); @@ -331,6 +322,16 @@ Workspace::Workspace(const Json::Value &config, WorkspaceManager &manager, // parse configuration + const auto &config_active_only = config["active-only"]; + if (config_active_only.isBool()) { + active_only_ = config_active_only.asBool(); + } + + const auto &config_ignore_hidden = config["ignore-hidden"]; + if (config_ignore_hidden.isBool()) { + ignore_hidden_ = config_ignore_hidden.asBool(); + } + const auto &config_format = config["format"]; format_ = config_format.isString() ? config_format.asString() : "{name}"; with_icon_ = format_.find("{icon}") != std::string::npos; @@ -375,38 +376,33 @@ Workspace::~Workspace() { spdlog::debug("[ext/workspaces]: Workspace {} destroyed", id_); } -bool Workspace::is_active() const { - return (state_ & EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE) == EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE; -} - -bool Workspace::is_urgent() const { - return (state_ & EXT_WORKSPACE_HANDLE_V1_STATE_URGENT) == EXT_WORKSPACE_HANDLE_V1_STATE_URGENT; -} - -bool Workspace::is_hidden() const { - return (state_ & EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN) == EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN; -} - void Workspace::update() { if (!needs_updating_) { return; } - // update style + // update style and visibility + const auto style_context = button_.get_style_context(); style_context->remove_class("active"); style_context->remove_class("urgent"); style_context->remove_class("hidden"); - if (is_active()) { + if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) { + button_.set_visible(true); style_context->add_class("active"); } - if (is_urgent()) { + if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_URGENT)) { + button_.set_visible(true); style_context->add_class("urgent"); } - if (is_hidden()) { + if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN)) { + button_.set_visible(!active_only_ && !ignore_hidden_); style_context->add_class("hidden"); } + if (state_ == 0) { + button_.set_visible(!active_only_); + } // update label label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("name", name_), @@ -487,7 +483,7 @@ bool Workspace::handle_clicked(const GdkEventButton *button) const { } std::string Workspace::icon() { - if (is_active()) { + if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) { const auto active_icon_it = icon_map_.find("active"); if (active_icon_it != icon_map_.end()) { return active_icon_it->second; From 6c48db6cee75609938e0524fcd59dc1997c03efb Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Mon, 23 Jun 2025 23:02:25 +0200 Subject: [PATCH 4/4] ext/workspaces: log workspace id/name This should help to distinguish between internal and external ID in the logs. --- src/modules/ext/workspace_manager.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/modules/ext/workspace_manager.cpp b/src/modules/ext/workspace_manager.cpp index 54750bb29..5fec3cdb6 100644 --- a/src/modules/ext/workspace_manager.cpp +++ b/src/modules/ext/workspace_manager.cpp @@ -281,10 +281,10 @@ bool WorkspaceGroup::has_workspace(const ext_workspace_handle_v1 *workspace) { } void WorkspaceGroup::handle_capabilities(uint32_t capabilities) { - spdlog::debug("[ext/workspaces]: Capabilities for workspace group {}:", id_); + spdlog::debug("[ext/workspaces]: Capabilities for workspace group {}:", id_); if ((capabilities & EXT_WORKSPACE_GROUP_HANDLE_V1_GROUP_CAPABILITIES_CREATE_WORKSPACE) == capabilities) { - spdlog::debug("[ext/workspaces]: create-workspace"); + spdlog::debug("[ext/workspaces]: - create-workspace"); } } @@ -413,12 +413,14 @@ void Workspace::update() { } void Workspace::handle_id(const std::string &id) { + spdlog::debug("[ext/workspaces]: ID for workspace {}: {}", id_, id); workspace_id_ = id; needs_updating_ = true; workspace_manager_.set_needs_sorting(); } void Workspace::handle_name(const std::string &name) { + spdlog::debug("[ext/workspaces]: Name for workspace {}: {}", id_, name); name_ = name; needs_updating_ = true; workspace_manager_.set_needs_sorting(); @@ -436,18 +438,18 @@ void Workspace::handle_state(uint32_t state) { } void Workspace::handle_capabilities(uint32_t capabilities) { - spdlog::debug("[ext/workspaces]: Capabilities for workspace {}:", id_); + spdlog::debug("[ext/workspaces]: Capabilities for workspace {}:", id_); if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE) == capabilities) { - spdlog::debug("[ext/workspaces]: activate"); + spdlog::debug("[ext/workspaces]: - activate"); } if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE) == capabilities) { - spdlog::debug("[ext/workspaces]: deactivate"); + spdlog::debug("[ext/workspaces]: - deactivate"); } if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_REMOVE) == capabilities) { - spdlog::debug("[ext/workspaces]: remove"); + spdlog::debug("[ext/workspaces]: - remove"); } if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ASSIGN) == capabilities) { - spdlog::debug("[ext/workspaces]: assign"); + spdlog::debug("[ext/workspaces]: - assign"); } needs_updating_ = true; }