add river/layout
This commit is contained in:
		
							parent
							
								
									c692d7bf64
								
							
						
					
					
						commit
						757a450324
					
				|  | @ -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" | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  | @ -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) | ||||
|  | @ -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)* | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -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', | ||||
|  |  | |||
|  | @ -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. | ||||
|  | @ -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. | ||||
|  | @ -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"> | ||||
|  |  | |||
|  | @ -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") { | ||||
|  |  | |||
|  | @ -0,0 +1,171 @@ | |||
| #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, ®istry_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(); | ||||
|   label_.set_markup(""); | ||||
|   ALabel::update(); | ||||
| 
 | ||||
|   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 */ | ||||
		Loading…
	
		Reference in New Issue