Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add river/layout #1991

Merged
merged 4 commits into from
Mar 1, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/factory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "modules/river/mode.hpp"
#include "modules/river/tags.hpp"
#include "modules/river/window.hpp"
#include "modules/river/layout.hpp"
#endif
#ifdef HAVE_HYPRLAND
#include "modules/hyprland/backend.hpp"
Expand Down
33 changes: 33 additions & 0 deletions include/modules/river/layout.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include <wayland-client.h>

#include "ALabel.hpp"
#include "bar.hpp"
#include "river-status-unstable-v1-client-protocol.h"

namespace waybar::modules::river {

class Layout : public waybar::ALabel {
public:
Layout(const std::string &, const waybar::Bar &, const Json::Value &);
~Layout();

// Handlers for wayland events
void handle_name(const char *name);
void handle_clear();
void handle_focused_output(struct wl_output *output);
void handle_unfocused_output(struct wl_output *output);

struct zriver_status_manager_v1 *status_manager_;
struct wl_seat *seat_;

private:
const waybar::Bar &bar_;
struct wl_output *output_; // stores the output this module belongs to
struct wl_output *focused_output_; // stores the currently focused output
struct zriver_output_status_v1 *output_status_;
struct zriver_seat_status_v1 *seat_status_;
};

} /* namespace waybar::modules::river */
67 changes: 67 additions & 0 deletions man/waybar-river-layout.5.scd
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
waybar-river-layout(5)

# NAME

waybar - river layout module

# DESCRIPTION

The *layout* module displays the current layout in river.

It may not be set until a layout is first applied.

# CONFIGURATION

Addressed by *river/layout*

*format*: ++
typeof: string ++
default: {} ++
The format, how information should be displayed. On {} data gets inserted.

*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.

*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.

*min-length*: ++
typeof: integer ++
The minimum length in characters the module should take up.

*align*: ++
typeof: float ++
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.

*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.

*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.

*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.

# EXAMPLE

```
"river/layout": {
"format": "{}",
"min-length": 4,
"align": "right"
}
```

# STYLE

- *#layout*
- *#layout.focused* Applied when the output this module's bar belongs to is focused.

# SEE ALSO

waybar(5), river(1)
1 change: 1 addition & 0 deletions man/waybar.5.scd.in
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ A module group is defined by specifying a module named "group/some-group-name".
- *waybar-river-mode(5)*
- *waybar-river-tags(5)*
- *waybar-river-window(5)*
- *waybar-river-layout(5)*
- *waybar-states(5)*
- *waybar-sway-mode(5)*
- *waybar-sway-scratchpad(5)*
Expand Down
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ if true
src_files += 'src/modules/river/mode.cpp'
src_files += 'src/modules/river/tags.cpp'
src_files += 'src/modules/river/window.cpp'
src_files += 'src/modules/river/layout.cpp'
endif

if true
Expand Down Expand Up @@ -404,6 +405,7 @@ if scdoc.found()
'waybar-river-mode.5.scd',
'waybar-river-tags.5.scd',
'waybar-river-window.5.scd',
'waybar-river-layout.5.scd',
'waybar-sway-language.5.scd',
'waybar-sway-mode.5.scd',
'waybar-sway-scratchpad.5.scd',
Expand Down
19 changes: 17 additions & 2 deletions protocol/river-status-unstable-v1.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</copyright>

<interface name="zriver_status_manager_v1" version="3">
<interface name="zriver_status_manager_v1" version="4">
<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.
Expand Down Expand Up @@ -47,7 +47,7 @@
</request>
</interface>

<interface name="zriver_output_status_v1" version="2">
<interface name="zriver_output_status_v1" version="4">
<description summary="track output tags and focus">
This interface allows clients to receive information about the current
windowing state of an output.
Expand Down Expand Up @@ -83,6 +83,21 @@
</description>
<arg name="tags" type="uint" summary="32-bit bitfield"/>
</event>

<event name="layout_name" since="4">
<description summary="name of the layout">
Sent once on binding the interface should a layout name exist and again
whenever the name changes.
</description>
<arg name="name" type="string" summary="layout name"/>
</event>

<event name="layout_name_clear" since="4">
<description summary="name of the layout">
Sent when the current layout name has been removed without a new one
being set, for example when the active layout generator disconnects.
</description>
</event>
</interface>

<interface name="zriver_seat_status_v1" version="3">
Expand Down
3 changes: 3 additions & 0 deletions src/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
if (ref == "river/window") {
return new waybar::modules::river::Window(id, bar_, config_[name]);
}
if (ref == "river/layout") {
return new waybar::modules::river::Layout(id, bar_, config_[name]);
}
#endif
#ifdef HAVE_HYPRLAND
if (ref == "hyprland/window") {
Expand Down
174 changes: 174 additions & 0 deletions src/modules/river/layout.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#include "modules/river/layout.hpp"

#include <spdlog/spdlog.h>
#include <wayland-client.h>

#include "client.hpp"

namespace waybar::modules::river {

static void listen_focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1,
uint32_t tags) {
// Intentionally empty
}

static void listen_view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1,
struct wl_array *tags) {
// Intentionally empty
}

static void listen_urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1,
uint32_t tags) {
// Intentionally empty
}

static void listen_layout_name(void *data, struct zriver_output_status_v1 *zriver_output_status_v1,
const char *layout) {
static_cast<Layout *>(data)->handle_name(layout);
}

static void listen_layout_name_clear(void *data,
struct zriver_output_status_v1 *zriver_output_status_v1) {
static_cast<Layout *>(data)->handle_clear();
}

static void listen_focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
struct wl_output *output) {
static_cast<Layout *>(data)->handle_focused_output(output);
}

static void listen_unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
struct wl_output *output) {
static_cast<Layout *>(data)->handle_unfocused_output(output);
}

static void listen_focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
const char *title) {
// Intentionally empty
}

static void listen_mode(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
const char *mode) {
// Intentionally empty
}

static const zriver_output_status_v1_listener output_status_listener_impl{
.focused_tags = listen_focused_tags,
.view_tags = listen_view_tags,
.urgent_tags = listen_urgent_tags,
.layout_name = listen_layout_name,
.layout_name_clear = listen_layout_name_clear,
};

static const zriver_seat_status_v1_listener seat_status_listener_impl{
.focused_output = listen_focused_output,
.unfocused_output = listen_unfocused_output,
.focused_view = listen_focused_view,
.mode = listen_mode,
};

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) {
version = std::min<uint32_t>(version, 4);

// implies ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_CLEAR_SINCE_VERSION
if (version < ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_SINCE_VERSION) {
spdlog::error(
"river server does not support the \"layout_name\" and \"layout_clear\" events; the "
"module will be disabled" +
std::to_string(version));
return;
}
static_cast<Layout *>(data)->status_manager_ = static_cast<struct zriver_status_manager_v1 *>(
wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, version));
}

if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min<uint32_t>(version, 1);
static_cast<Layout *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}

static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
// Nobody cares
}

static const wl_registry_listener registry_listener_impl = {.global = handle_global,
.global_remove = handle_global_remove};

Layout::Layout(const std::string &id, const waybar::Bar &bar, const Json::Value &config)
: waybar::ALabel(config, "layout", id, "{}"),
status_manager_{nullptr},
seat_{nullptr},
bar_(bar),
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);

output_ = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());

if (!status_manager_) {
spdlog::error("river_status_manager_v1 not advertised");
return;
}

if (!seat_) {
spdlog::error("wl_seat not advertised");
}

label_.hide();
ALabel::update();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not initially hide the label. I did notice the same behaviour from window... any ideas?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe because the label isn't empty, and so ALabel::update re-show it 🤔


seat_status_ = zriver_status_manager_v1_get_river_seat_status(status_manager_, seat_);
zriver_seat_status_v1_add_listener(seat_status_, &seat_status_listener_impl, this);

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_);
}

Layout::~Layout() {
if (output_status_) {
zriver_output_status_v1_destroy(output_status_);
}
if (seat_status_) {
zriver_seat_status_v1_destroy(seat_status_);
}
}

void Layout::handle_name(const char *name) {
if (std::strcmp(name, "") == 0 || format_.empty()) {
label_.hide(); // hide empty labels or labels with empty format
} else {
label_.show();
label_.set_markup(fmt::format(format_, Glib::Markup::escape_text(name).raw()));
}
ALabel::update();
}

void Layout::handle_clear() {
label_.hide();
ALabel::update();
}

void Layout::handle_focused_output(struct wl_output *output) {
if (output_ == output) { // if we focused the output this bar belongs to
label_.get_style_context()->add_class("focused");
ALabel::update();
}
focused_output_ = output;
}

void Layout::handle_unfocused_output(struct wl_output *output) {
if (output_ == output) { // if we unfocused the output this bar belongs to
label_.get_style_context()->remove_class("focused");
ALabel::update();
}
}

} /* namespace waybar::modules::river */