From 2b11b7ef8c9b66e9f00ec573fa3de3f14e724e0d Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 5 Aug 2020 02:17:38 +0300 Subject: [PATCH] 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 fcbf3a2d..db6fd875 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 00000000..351ddc45 --- /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 dd56c290..d0ba1e81 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 07d524ae..467260bc 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 00000000..ae0e44db --- /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 5a01d523..804378e2 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 00000000..0d9ae0b3 --- /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