Base wlr impl (Manager, Group)

This commit is contained in:
dmitry 2020-08-05 02:17:38 +03:00
parent 0625bc7688
commit 2b11b7ef8c
7 changed files with 510 additions and 0 deletions

View File

@ -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"

View File

@ -0,0 +1,82 @@
#pragma once
#include <fmt/format.h>
#include <gtkmm/image.h>
#include <memory>
#include <vector>
#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<std::unique_ptr<Workspace>> 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<std::unique_ptr<WorkspaceGroup>> groups_;
// wlr stuff
zwlr_workspace_manager_v1 *workspace_manager_ = nullptr;
};
} // namespace waybar::modules::wlr

View File

@ -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

View File

@ -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'],
]

View File

@ -0,0 +1,270 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_workspace_unstable_v1">
<copyright>
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.
</copyright>
<interface name="zwlr_workspace_manager_v1" version="1">
<description summary="list and control workspaces">
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.
</description>
<event name="workspace_group">
<description summary="a workspace group has been created">
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.
</description>
<arg name="workspace_group" type="new_id" interface="zwlr_workspace_group_handle_v1"/>
</event>
<request name="commit">
<description summary="all requests about the workspaces have been sent">
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.
</description>
</request>
<event name="done">
<description summary="all information about the workspace groups has been sent">
This event is sent after all changes in all workspace groups have been
sent.
This allows changes to one or more zwlr_workspace_group_handle_v1
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.
</description>
</event>
<event name="finished">
<description summary="the compositor has finished with the workspace_manager">
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.
</description>
</event>
<request name="stop">
<description summary="stop sending events">
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.
</description>
</request>
</interface>
<interface name="zwlr_workspace_group_handle_v1" version="1">
<description summary="a workspace group assigned to a set of outputs">
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.
</description>
<event name="output_enter">
<description summary="output assigned to workspace group">
This event is emitted whenever an output is assigned to the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="output_leave">
<description summary="output removed from workspace group">
This event is emitted whenever an output is removed from the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="workspace">
<description summary="workspace added to 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.
</description>
<arg name="workspace" type="new_id" interface="zwlr_workspace_handle_v1"/>
</event>
<event name="remove">
<description summary="this workspace group has been destroyed">
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.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the zwlr_workspace_handle_v1 object">
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.
</description>
</request>
</interface>
<interface name="zwlr_workspace_handle_v1" version="1">
<description summary="a workspace handing a group of surfaces">
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.
</description>
<event name="name">
<description summary="workspace name changed">
This event is emitted immediately after the zwlr_workspace_handle_v1 is
created and whenever the name of the workspace changes.
</description>
<arg name="name" type="string"/>
</event>
<event name="coordinates">
<description summary="workspace coordinates changed">
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.
</description>
<arg name="coordinates" type="array"/>
</event>
<event name="state">
<description summary="the state of the workspace changed">
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.
</description>
<arg name="state" type="array"/>
</event>
<enum name="state">
<description summary="types of states on the workspace">
The different states that a workspace can have.
</description>
<entry name="active" value="0" summary="the workspace is active"/>
</enum>
<event name="remove">
<description summary="this workspace has been destroyed">
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.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the zwlr_workspace_handle_v1 object">
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.
</description>
</request>
<request name="activate">
<description summary="activate the workspace">
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.
</description>
</request>
<request name="deactivate">
<description summary="activate the workspace">
Request that this workspace be deactivated.
There is no guarantee the workspace will be actually deactivated.
</description>
</request>
</interface>
</protocol>

View File

@ -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") {

View File

@ -0,0 +1,152 @@
#include "modules/wlr/workspaces.hpp"
#include <gtkmm.h>
#include <spdlog/spdlog.h>
#include <client.hpp>
namespace waybar::modules::wlr {
static void handle_global(void *data, wl_registry *registry, uint32_t name,
const char *interface, uint32_t version) {
if (std::strcmp(interface, zwlr_workspace_manager_v1_interface.name) == 0) {
static_cast<WorkspaceManager *>(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, &registry_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<WorkspaceManager *>(data)->handle_workspace_group_create(workspace_group);
}
static void workspace_manager_handle_done(void *data, zwlr_workspace_manager_v1 *_) {
static_cast<WorkspaceManager *>(data)->handle_done();
}
static void workspace_manager_handle_finished(void *data, zwlr_workspace_manager_v1 *_) {
static_cast<WorkspaceManager *>(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<zwlr_workspace_manager_v1 *>(
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<WorkspaceGroup>(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<WorkspaceGroup*>(data)->handle_output_enter(*output);
}
static void workspace_group_handle_output_leave(void *data,
zwlr_workspace_group_handle_v1 *_,
wl_output *output) {
static_cast<WorkspaceGroup*>(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<WorkspaceGroup*>(data)->handle_workspace_create(*workspace);
}
static void workspace_group_handle_remove(void *data,
zwlr_workspace_group_handle_v1 *_) {
static_cast<WorkspaceGroup*>(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<Workspace>(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