river/tags: add module

This commit is contained in:
Isaac Freund 2020-06-06 15:41:37 +02:00
parent 181fde254f
commit 343a8bef22
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
8 changed files with 319 additions and 1 deletions

View File

@ -10,6 +10,9 @@
#ifdef HAVE_WLR #ifdef HAVE_WLR
#include "modules/wlr/taskbar.hpp" #include "modules/wlr/taskbar.hpp"
#endif #endif
#ifdef HAVE_RIVER
#include "modules/river/tags.hpp"
#endif
#if defined(__linux__) && !defined(NO_FILESYSTEM) #if defined(__linux__) && !defined(NO_FILESYSTEM)
#include "modules/battery.hpp" #include "modules/battery.hpp"
#endif #endif

View File

@ -0,0 +1,31 @@
#pragma once
#include <gtkmm/button.h>
#include <wayland-client.h>
#include "AModule.hpp"
#include "bar.hpp"
#include "river-status-unstable-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
namespace waybar::modules::river {
class Tags : public waybar::AModule {
public:
Tags(const std::string &, const waybar::Bar &, const Json::Value &);
~Tags();
// Handlers for wayland events
void handle_focused_tags(uint32_t tags);
void handle_view_tags(struct wl_array *tags);
struct zriver_status_manager_v1 *status_manager_;
private:
const waybar::Bar & bar_;
Gtk::Box box_;
std::vector<Gtk::Button> buttons_;
struct zriver_output_status_v1 *output_status_;
};
} /* namespace waybar::modules::river */

View File

@ -0,0 +1,38 @@
waybar-river-tags(5)
# NAME
waybar - river tags module
# DESCRIPTION
The *tags* module displays the current state of tags in river.
# CONFIGURATION
Addressed by *river/tags*
*num-tags*: ++
typeof: uint ++
default: 9 ++
The number of tags that should be displayed.
# EXAMPLE
```
"river/tags": {
"num-tags": 5
}
```
# STYLE
- *#tags button*
- *#tags button.occupied*
- *#tags button.focused*
Note that a tag can be both occupied and focused at the same time.
# SEE ALSO
waybar(5), river(1)

View File

@ -170,6 +170,11 @@ if true
src_files += 'src/modules/wlr/taskbar.cpp' src_files += 'src/modules/wlr/taskbar.cpp'
endif endif
if true
add_project_arguments('-DHAVE_RIVER', language: 'cpp')
src_files += 'src/modules/river/tags.cpp'
endif
if libnl.found() and libnlgen.found() if libnl.found() and libnlgen.found()
add_project_arguments('-DHAVE_LIBNL', language: 'cpp') add_project_arguments('-DHAVE_LIBNL', language: 'cpp')
src_files += 'src/modules/network.cpp' src_files += 'src/modules/network.cpp'
@ -259,6 +264,7 @@ if scdoc.found()
'waybar-mpd.5.scd', 'waybar-mpd.5.scd',
'waybar-network.5.scd', 'waybar-network.5.scd',
'waybar-pulseaudio.5.scd', 'waybar-pulseaudio.5.scd',
'waybar-river-tags.5.scd',
'waybar-sway-mode.5.scd', 'waybar-sway-mode.5.scd',
'waybar-sway-window.5.scd', 'waybar-sway-window.5.scd',
'waybar-sway-workspaces.5.scd', 'waybar-sway-workspaces.5.scd',

View File

@ -27,6 +27,7 @@ client_protocols = [
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'],
['wlr-foreign-toplevel-management-unstable-v1.xml'], ['wlr-foreign-toplevel-management-unstable-v1.xml'],
['river-status-unstable-v1.xml'],
] ]
client_protos_src = [] client_protos_src = []

View File

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="river_status_unstable_v1">
<copyright>
Copyright 2020 Isaac Freund
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</copyright>
<interface name="zriver_status_manager_v1" version="1">
<description summary="manage river status objects">
A global factory for objects that receive status information specific
to river. It could be used to implement, for example, a status bar.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the river_status_manager object">
This request indicates that the client will not use the
river_status_manager object any more. Objects that have been created
through this instance are not affected.
</description>
</request>
<request name="get_river_output_status">
<description summary="create an output status object">
This creates a new river_output_status object for the given wl_output.
</description>
<arg name="id" type="new_id" interface="zriver_output_status_v1"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="get_river_seat_status">
<description summary="create a seat status object">
This creates a new river_seat_status object for the given wl_seat.
</description>
<arg name="id" type="new_id" interface="zriver_seat_status_v1"/>
<arg name="seat" type="object" interface="wl_seat"/>
</request>
</interface>
<interface name="zriver_output_status_v1" version="1">
<description summary="track output tags and focus">
This interface allows clients to receive information about the current
windowing state of an output.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the river_output_status object">
This request indicates that the client will not use the
river_output_status object any more.
</description>
</request>
<event name="focused_tags">
<description summary="focused tags of the output">
Sent once binding the interface and again whenever the tag focus of
the output changes.
</description>
<arg name="tags" type="uint" summary="32-bit bitfield"/>
</event>
<event name="view_tags">
<description summary="tag state of an output's views">
Sent once on binding the interface and again whenever the tag state
of the output changes.
</description>
<arg name="tags" type="array" summary="array of 32-bit bitfields"/>
</event>
</interface>
<interface name="zriver_seat_status_v1" version="1">
<description summary="track seat focus">
This interface allows clients to receive information about the current
focus of a seat.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the river_seat_status object">
This request indicates that the client will not use the
river_seat_status object any more.
</description>
</request>
<event name="focused_output">
<description summary="the seat focused an output">
Sent on binding the interface and again whenever an output gains focus.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="unfocused_output">
<description summary="the seat unfocused an output">
Sent whenever an output loses focus.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="focused_view">
<description summary="information on the focused view">
Sent once on binding the interface and again whenever the focused
view or a property thereof changes. The title may be an empty string
if no view is focused or the focused view did not set a title.
</description>
<arg name="title" type="string" summary="title of the focused view"/>
</event>
</interface>
</protocol>

View File

@ -27,6 +27,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
if (ref == "wlr/taskbar") { if (ref == "wlr/taskbar") {
return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]); return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]);
} }
#endif
#ifdef HAVE_RIVER
if (ref == "river/tags") {
return new waybar::modules::river::Tags(id, bar_, config_[name]);
}
#endif #endif
if (ref == "idle_inhibitor") { if (ref == "idle_inhibitor") {
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);

118
src/modules/river/tags.cpp Normal file
View File

@ -0,0 +1,118 @@
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include <spdlog/spdlog.h>
#include <wayland-client.h>
#include "client.hpp"
#include "modules/river/tags.hpp"
#include "river-status-unstable-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
namespace waybar::modules::river {
static void listen_focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1,
uint32_t tags) {
static_cast<Tags *>(data)->handle_focused_tags(tags);
}
static void listen_view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1,
struct wl_array *tags) {
static_cast<Tags *>(data)->handle_view_tags(tags);
}
static const zriver_output_status_v1_listener output_status_listener_impl{
.focused_tags = listen_focused_tags,
.view_tags = listen_view_tags,
};
static void handle_global(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version) {
if (std::strcmp(interface, zriver_status_manager_v1_interface.name) == 0) {
static_cast<Tags *>(data)->status_manager_ = static_cast<struct zriver_status_manager_v1 *>(
wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, version));
}
}
static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
/* Ignore event */
}
static const wl_registry_listener registry_listener_impl = {.global = handle_global,
.global_remove = handle_global_remove};
Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &config)
: waybar::AModule(config, "tags", id, false, false),
status_manager_{nullptr},
bar_(bar),
box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
output_status_{nullptr} {
struct wl_display * display = Client::inst()->wl_display;
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener_impl, this);
wl_display_roundtrip(display);
if (!status_manager_) {
spdlog::error("river_status_manager_v1 not advertised");
return;
}
box_.set_name("tags");
if (!id.empty()) {
box_.get_style_context()->add_class(id);
}
event_box_.add(box_);
// Default to 9 tags
const uint32_t num_tags = config["num-tags"].isUInt() ? config_["num-tags"].asUInt() : 9;
for (uint32_t tag = 1; tag <= num_tags; ++tag) {
Gtk::Button &button = buttons_.emplace_back(std::to_string(tag));
button.set_relief(Gtk::RELIEF_NONE);
box_.pack_start(button, false, false, 0);
button.show();
}
struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output);
zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this);
zriver_status_manager_v1_destroy(status_manager_);
}
Tags::~Tags() {
if (output_status_) {
zriver_output_status_v1_destroy(output_status_);
}
}
void Tags::handle_focused_tags(uint32_t tags) {
uint32_t i = 0;
for (auto &button : buttons_) {
if ((1 << i) & tags) {
button.get_style_context()->add_class("focused");
} else {
button.get_style_context()->remove_class("focused");
}
++i;
}
}
void Tags::handle_view_tags(struct wl_array *view_tags) {
// First clear all occupied state
for (auto &button : buttons_) {
button.get_style_context()->remove_class("occupied");
}
// Set tags with a view to occupied
uint32_t *start = static_cast<uint32_t *>(view_tags->data);
for (uint32_t *tags = start; tags < start + view_tags->size / sizeof(uint32_t); ++tags) {
uint32_t i = 0;
for (auto &button : buttons_) {
if (*tags & (1 << i)) {
button.get_style_context()->add_class("occupied");
}
++i;
}
}
}
} /* namespace waybar::modules::river */