Merge pull request #805 from Anakael/workspace-manager-implementation
WLR Workspace manager implementation
This commit is contained in:
		
						commit
						977d21b5f6
					
				| 
						 | 
					@ -14,6 +14,7 @@
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef HAVE_WLR
 | 
					#ifdef HAVE_WLR
 | 
				
			||||||
#include "modules/wlr/taskbar.hpp"
 | 
					#include "modules/wlr/taskbar.hpp"
 | 
				
			||||||
 | 
					#include "modules/wlr/workspace_manager.hpp"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef HAVE_RIVER
 | 
					#ifdef HAVE_RIVER
 | 
				
			||||||
#include "modules/river/tags.hpp"
 | 
					#include "modules/river/tags.hpp"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,160 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <fmt/format.h>
 | 
				
			||||||
 | 
					#include <gtkmm/button.h>
 | 
				
			||||||
 | 
					#include <gtkmm/image.h>
 | 
				
			||||||
 | 
					#include <gtkmm/label.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <functional>
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "AModule.hpp"
 | 
				
			||||||
 | 
					#include "bar.hpp"
 | 
				
			||||||
 | 
					#include "ext-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,
 | 
				
			||||||
 | 
					            zext_workspace_handle_v1 *workspace, uint32_t id);
 | 
				
			||||||
 | 
					  ~Workspace();
 | 
				
			||||||
 | 
					  auto update() -> void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto id() const -> uint32_t { return id_; }
 | 
				
			||||||
 | 
					  auto is_active() const -> bool { return state_ & static_cast<uint32_t>(State::ACTIVE); }
 | 
				
			||||||
 | 
					  auto is_urgent() const -> bool { return state_ & static_cast<uint32_t>(State::URGENT); }
 | 
				
			||||||
 | 
					  auto is_hidden() const -> bool { return state_ & static_cast<uint32_t>(State::HIDDEN); }
 | 
				
			||||||
 | 
					  // wlr stuff
 | 
				
			||||||
 | 
					  auto handle_name(const std::string &name) -> void;
 | 
				
			||||||
 | 
					  auto handle_coordinates(const std::vector<uint32_t> &coordinates) -> void;
 | 
				
			||||||
 | 
					  auto handle_state(const std::vector<uint32_t> &state) -> void;
 | 
				
			||||||
 | 
					  auto handle_remove() -> void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto handle_done() -> void;
 | 
				
			||||||
 | 
					  auto handle_clicked(GdkEventButton *bt) -> bool;
 | 
				
			||||||
 | 
					  auto show() -> void;
 | 
				
			||||||
 | 
					  auto hide() -> void;
 | 
				
			||||||
 | 
					  auto get_button_ref() -> Gtk::Button & { return button_; }
 | 
				
			||||||
 | 
					  auto get_name() -> std::string & { return name_; }
 | 
				
			||||||
 | 
					  auto get_coords() -> std::vector<uint32_t> & { return coordinates_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  enum class State {
 | 
				
			||||||
 | 
					    ACTIVE = (1 << 0),
 | 
				
			||||||
 | 
					    URGENT = (1 << 1),
 | 
				
			||||||
 | 
					    HIDDEN = (1 << 2),
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  auto get_icon() -> std::string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Bar         &bar_;
 | 
				
			||||||
 | 
					  const Json::Value &config_;
 | 
				
			||||||
 | 
					  WorkspaceGroup    &workspace_group_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // wlr stuff
 | 
				
			||||||
 | 
					  zext_workspace_handle_v1 *workspace_handle_;
 | 
				
			||||||
 | 
					  uint32_t                  state_ = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t                                  id_;
 | 
				
			||||||
 | 
					  std::string                               name_;
 | 
				
			||||||
 | 
					  std::vector<uint32_t>                     coordinates_;
 | 
				
			||||||
 | 
					  static std::map<std::string, std::string> icons_map_;
 | 
				
			||||||
 | 
					  std::string                               format_;
 | 
				
			||||||
 | 
					  bool                                      with_icon_ = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Gtk::Button button_;
 | 
				
			||||||
 | 
					  Gtk::Box    content_;
 | 
				
			||||||
 | 
					  Gtk::Label  label_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WorkspaceGroup {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  WorkspaceGroup(const waybar::Bar &bar, Gtk::Box &box, const Json::Value &config,
 | 
				
			||||||
 | 
					                 WorkspaceManager &manager, zext_workspace_group_handle_v1 *workspace_group_handle,
 | 
				
			||||||
 | 
					                 uint32_t id);
 | 
				
			||||||
 | 
					  ~WorkspaceGroup();
 | 
				
			||||||
 | 
					  auto update() -> void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto id() const -> uint32_t { return id_; }
 | 
				
			||||||
 | 
					  auto is_visible() const -> bool;
 | 
				
			||||||
 | 
					  auto remove_workspace(uint32_t id_) -> void;
 | 
				
			||||||
 | 
					  auto active_only() const -> bool;
 | 
				
			||||||
 | 
					  auto creation_delayed() const -> bool;
 | 
				
			||||||
 | 
					  auto workspaces() -> std::vector<std::unique_ptr<Workspace>> & { return workspaces_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto sort_workspaces() -> void;
 | 
				
			||||||
 | 
					  auto set_need_to_sort() -> void { need_to_sort = true; }
 | 
				
			||||||
 | 
					  auto add_button(Gtk::Button &button) -> void;
 | 
				
			||||||
 | 
					  auto remove_button(Gtk::Button &button) -> void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // wlr stuff
 | 
				
			||||||
 | 
					  auto handle_workspace_create(zext_workspace_handle_v1 *workspace_handle) -> void;
 | 
				
			||||||
 | 
					  auto handle_remove() -> void;
 | 
				
			||||||
 | 
					  auto handle_output_enter(wl_output *output) -> void;
 | 
				
			||||||
 | 
					  auto handle_output_leave() -> void;
 | 
				
			||||||
 | 
					  auto handle_done() -> void;
 | 
				
			||||||
 | 
					  auto commit() -> void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  static uint32_t    workspace_global_id;
 | 
				
			||||||
 | 
					  const waybar::Bar &bar_;
 | 
				
			||||||
 | 
					  Gtk::Box          &box_;
 | 
				
			||||||
 | 
					  const Json::Value &config_;
 | 
				
			||||||
 | 
					  WorkspaceManager  &workspace_manager_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // wlr stuff
 | 
				
			||||||
 | 
					  zext_workspace_group_handle_v1 *workspace_group_handle_;
 | 
				
			||||||
 | 
					  wl_output                      *output_ = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t                                id_;
 | 
				
			||||||
 | 
					  std::vector<std::unique_ptr<Workspace>> workspaces_;
 | 
				
			||||||
 | 
					  bool                                    need_to_sort = false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WorkspaceManager : public AModule {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config);
 | 
				
			||||||
 | 
					  ~WorkspaceManager() override;
 | 
				
			||||||
 | 
					  auto update() -> void override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto all_outputs() const -> bool { return all_outputs_; }
 | 
				
			||||||
 | 
					  auto active_only() const -> bool { return active_only_; }
 | 
				
			||||||
 | 
					  auto workspace_comparator() const
 | 
				
			||||||
 | 
					      -> std::function<bool(std::unique_ptr<Workspace> &, std::unique_ptr<Workspace> &)>;
 | 
				
			||||||
 | 
					  auto creation_delayed() const -> bool { return creation_delayed_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto sort_workspaces() -> void;
 | 
				
			||||||
 | 
					  auto remove_workspace_group(uint32_t id_) -> void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // wlr stuff
 | 
				
			||||||
 | 
					  auto register_manager(wl_registry *registry, uint32_t name, uint32_t version) -> void;
 | 
				
			||||||
 | 
					  auto handle_workspace_group_create(zext_workspace_group_handle_v1 *workspace_group_handle)
 | 
				
			||||||
 | 
					      -> void;
 | 
				
			||||||
 | 
					  auto handle_done() -> void;
 | 
				
			||||||
 | 
					  auto handle_finished() -> void;
 | 
				
			||||||
 | 
					  auto commit() -> void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  const waybar::Bar                           &bar_;
 | 
				
			||||||
 | 
					  Gtk::Box                                     box_;
 | 
				
			||||||
 | 
					  std::vector<std::unique_ptr<WorkspaceGroup>> groups_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // wlr stuff
 | 
				
			||||||
 | 
					  zext_workspace_manager_v1 *workspace_manager_ = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static uint32_t group_global_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool sort_by_name_ = true;
 | 
				
			||||||
 | 
					  bool sort_by_coordinates_ = true;
 | 
				
			||||||
 | 
					  bool all_outputs_ = false;
 | 
				
			||||||
 | 
					  bool active_only_ = false;
 | 
				
			||||||
 | 
					  bool creation_delayed_ = false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace waybar::modules::wlr
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					#include "ext-workspace-unstable-v1-client-protocol.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace waybar::modules::wlr {
 | 
				
			||||||
 | 
					  void add_registry_listener(void *data);
 | 
				
			||||||
 | 
					  void add_workspace_listener(zext_workspace_handle_v1 *workspace_handle, void *data);
 | 
				
			||||||
 | 
					  void add_workspace_group_listener(zext_workspace_group_handle_v1 *workspace_group_handle, void *data);
 | 
				
			||||||
 | 
					  zext_workspace_manager_v1* workspace_manager_bind(wl_registry *registry, uint32_t name, uint32_t version, void *data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,87 @@
 | 
				
			||||||
 | 
					waybar-wlr-workspaces(5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# NAME
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					waybar - wlr workspaces module
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# DESCRIPTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The *workspaces* module displays the currently used workspaces in wayland compositor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# CONFIGURATION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Addressed by *wlr/workspaces*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*format*: ++
 | 
				
			||||||
 | 
						typeof: string ++
 | 
				
			||||||
 | 
						default: {name} ++
 | 
				
			||||||
 | 
						The format, how information should be displayed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*format-icons*: ++
 | 
				
			||||||
 | 
						typeof: array ++
 | 
				
			||||||
 | 
						Based on the workspace name and state, the corresponding icon gets selected. See *icons*.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*sort-by-name*: ++
 | 
				
			||||||
 | 
						typeof: bool ++
 | 
				
			||||||
 | 
						default: true ++
 | 
				
			||||||
 | 
						Should workspaces be sorted by name.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*sort-by-coordinates*: ++
 | 
				
			||||||
 | 
						typeof: bool ++
 | 
				
			||||||
 | 
						default: true ++
 | 
				
			||||||
 | 
						Should workspaces be sorted by coordinates.
 | 
				
			||||||
 | 
						Note that if both  *sort-by-name* and *sort-by-coordinates* are true sort by name will be first.
 | 
				
			||||||
 | 
						If both are false - sort by id will be performed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*all-outputs*: ++
 | 
				
			||||||
 | 
						typeof: bool ++
 | 
				
			||||||
 | 
						default: false ++
 | 
				
			||||||
 | 
						If set to false workspaces group will be shown only in assigned output. Otherwise all workspace groups are shown.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*active-only*: ++
 | 
				
			||||||
 | 
						typeof: bool ++
 | 
				
			||||||
 | 
						default: false ++
 | 
				
			||||||
 | 
						If set to true only active or urgent workspaces will be shown.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# FORMAT REPLACEMENTS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*{name}*: Name of workspace assigned by compositor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*{icon}*: Icon, as defined in *format-icons*.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# CLICK ACTIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*activate*: Switch to workspace.
 | 
				
			||||||
 | 
					*close*: Close the workspace.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ICONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Additional to workspace name matching, the following *format-icons* can be set.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- *default*: Will be shown, when no string match is found.
 | 
				
			||||||
 | 
					- *focused*: Will be shown, when workspace is focused
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# EXAMPLES
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					"wlr/workspaces": {
 | 
				
			||||||
 | 
						"format": "{name}: {icon}",
 | 
				
			||||||
 | 
						"format-icons": {
 | 
				
			||||||
 | 
							"1": "",
 | 
				
			||||||
 | 
							"2": "",
 | 
				
			||||||
 | 
							"3": "",
 | 
				
			||||||
 | 
							"4": "",
 | 
				
			||||||
 | 
							"5": "",
 | 
				
			||||||
 | 
							"focused": "",
 | 
				
			||||||
 | 
							"default": ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Style
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- *#workspaces*
 | 
				
			||||||
 | 
					- *#workspaces button*
 | 
				
			||||||
 | 
					- *#workspaces button.active*
 | 
				
			||||||
 | 
					- *#workspaces button.urgent*
 | 
				
			||||||
 | 
					- *#workspaces button.hidden*
 | 
				
			||||||
| 
						 | 
					@ -224,5 +224,6 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270.
 | 
				
			||||||
- *waybar-sway-window(5)*
 | 
					- *waybar-sway-window(5)*
 | 
				
			||||||
- *waybar-sway-workspaces(5)*
 | 
					- *waybar-sway-workspaces(5)*
 | 
				
			||||||
- *waybar-wlr-taskbar(5)*
 | 
					- *waybar-wlr-taskbar(5)*
 | 
				
			||||||
 | 
					- *waybar-wlr-workspaces(5)*
 | 
				
			||||||
- *waybar-temperature(5)*
 | 
					- *waybar-temperature(5)*
 | 
				
			||||||
- *waybar-tray(5)*
 | 
					- *waybar-tray(5)*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -186,6 +186,8 @@ src_files += [
 | 
				
			||||||
if true
 | 
					if true
 | 
				
			||||||
    add_project_arguments('-DHAVE_WLR', language: 'cpp')
 | 
					    add_project_arguments('-DHAVE_WLR', language: 'cpp')
 | 
				
			||||||
    src_files += 'src/modules/wlr/taskbar.cpp'
 | 
					    src_files += 'src/modules/wlr/taskbar.cpp'
 | 
				
			||||||
 | 
					    src_files += 'src/modules/wlr/workspace_manager.cpp'
 | 
				
			||||||
 | 
					    src_files += 'src/modules/wlr/workspace_manager_binding.cpp'
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if true
 | 
					if true
 | 
				
			||||||
| 
						 | 
					@ -334,6 +336,7 @@ if scdoc.found()
 | 
				
			||||||
        'waybar-tray.5.scd',
 | 
					        'waybar-tray.5.scd',
 | 
				
			||||||
        'waybar-states.5.scd',
 | 
					        'waybar-states.5.scd',
 | 
				
			||||||
        'waybar-wlr-taskbar.5.scd',
 | 
					        'waybar-wlr-taskbar.5.scd',
 | 
				
			||||||
 | 
					        'waybar-wlr-workspaces.5.scd',
 | 
				
			||||||
        'waybar-bluetooth.5.scd',
 | 
					        'waybar-bluetooth.5.scd',
 | 
				
			||||||
        'waybar-sndio.5.scd',
 | 
					        'waybar-sndio.5.scd',
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,306 @@
 | 
				
			||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<protocol name="ext_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="zext_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 zext_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
 | 
				
			||||||
 | 
					        zext_workspace_group_handle_v1.
 | 
				
			||||||
 | 
					      </description>
 | 
				
			||||||
 | 
					      <arg name="workspace_group" type="new_id" interface="zext_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 zext_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 zext_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
 | 
				
			||||||
 | 
					        zext_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
 | 
				
			||||||
 | 
					        zext_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="zext_workspace_group_handle_v1" version="1">
 | 
				
			||||||
 | 
					    <description summary="a workspace group assigned to a set of outputs">
 | 
				
			||||||
 | 
					      A zext_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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      For example, a compositor which has a set of workspaces for each output may
 | 
				
			||||||
 | 
					      advertise a workspace group (and its workspaces) per output, whereas a compositor
 | 
				
			||||||
 | 
					      where a workspace spans all outputs may advertise a single workspace group for all
 | 
				
			||||||
 | 
					      outputs.
 | 
				
			||||||
 | 
					    </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
 | 
				
			||||||
 | 
					        zext_workspace_handle_v1.
 | 
				
			||||||
 | 
					      </description>
 | 
				
			||||||
 | 
					      <arg name="workspace" type="new_id" interface="zext_workspace_handle_v1"/>
 | 
				
			||||||
 | 
					    </event>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <event name="remove">
 | 
				
			||||||
 | 
					      <description summary="this workspace group has been destroyed">
 | 
				
			||||||
 | 
					        This event means the zext_workspace_group_handle_v1 has been destroyed.
 | 
				
			||||||
 | 
					        It is guaranteed there won't be any more events for this
 | 
				
			||||||
 | 
					        zext_workspace_group_handle_v1. The zext_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="create_workspace">
 | 
				
			||||||
 | 
					      <description summary="create a new workspace">
 | 
				
			||||||
 | 
					        Request that the compositor create a new workspace with the given name.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        There is no guarantee that the compositor will create a new workspace,
 | 
				
			||||||
 | 
					        or that the created workspace will have the provided name.
 | 
				
			||||||
 | 
					      </description>
 | 
				
			||||||
 | 
					      <arg name="workspace" type="string"/>
 | 
				
			||||||
 | 
					    </request>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <request name="destroy" type="destructor">
 | 
				
			||||||
 | 
					      <description summary="destroy the zext_workspace_handle_v1 object">
 | 
				
			||||||
 | 
					        Destroys the zext_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="zext_workspace_handle_v1" version="1">
 | 
				
			||||||
 | 
					    <description summary="a workspace handing a group of surfaces">
 | 
				
			||||||
 | 
					      A zext_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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Each workspace can belong to only a single workspace group.
 | 
				
			||||||
 | 
					      Depepending on the compositor policy, there might be workspaces with
 | 
				
			||||||
 | 
					      the same name in different workspace groups, but these workspaces are still
 | 
				
			||||||
 | 
					      separate (e.g. one of them might be active while the other is not).
 | 
				
			||||||
 | 
					    </description>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <event name="name">
 | 
				
			||||||
 | 
					      <description summary="workspace name changed">
 | 
				
			||||||
 | 
					        This event is emitted immediately after the zext_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 zext_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 zext_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"/>
 | 
				
			||||||
 | 
					      <entry name="urgent" value="1" summary="the workspace requests attention"/>
 | 
				
			||||||
 | 
					      <entry name="hidden" value="2">
 | 
				
			||||||
 | 
					        <description summary="the workspace is not visible">
 | 
				
			||||||
 | 
					          The workspace is not visible in its workspace group, and clients
 | 
				
			||||||
 | 
					          attempting to visualize the compositor workspace state should not
 | 
				
			||||||
 | 
					          display such workspaces.
 | 
				
			||||||
 | 
					        </description>
 | 
				
			||||||
 | 
					      </entry>
 | 
				
			||||||
 | 
					    </enum>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <event name="remove">
 | 
				
			||||||
 | 
					      <description summary="this workspace has been destroyed">
 | 
				
			||||||
 | 
					        This event means the zext_workspace_handle_v1 has been destroyed. It is
 | 
				
			||||||
 | 
					        guaranteed there won't be any more events for this
 | 
				
			||||||
 | 
					        zext_workspace_handle_v1. The zext_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 zext_workspace_handle_v1 object">
 | 
				
			||||||
 | 
					        Destroys the zext_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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <request name="remove">
 | 
				
			||||||
 | 
					      <description summary="remove the workspace">
 | 
				
			||||||
 | 
					        Request that this workspace be removed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        There is no guarantee the workspace will be actually removed.
 | 
				
			||||||
 | 
					      </description>
 | 
				
			||||||
 | 
					    </request>
 | 
				
			||||||
 | 
					  </interface>
 | 
				
			||||||
 | 
					</protocol>
 | 
				
			||||||
| 
						 | 
					@ -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'],
 | 
				
			||||||
 | 
						['ext-workspace-unstable-v1.xml'],
 | 
				
			||||||
	['river-status-unstable-v1.xml'],
 | 
						['river-status-unstable-v1.xml'],
 | 
				
			||||||
	['river-control-unstable-v1.xml'],
 | 
						['river-control-unstable-v1.xml'],
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,9 @@ 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]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (ref == "wlr/workspaces") {
 | 
				
			||||||
 | 
					      return new waybar::modules::wlr::WorkspaceManager(id, bar_, config_[name]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef HAVE_RIVER
 | 
					#ifdef HAVE_RIVER
 | 
				
			||||||
    if (ref == "river/tags") {
 | 
					    if (ref == "river/tags") {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,472 @@
 | 
				
			||||||
 | 
					#include "modules/wlr/workspace_manager.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <gdk/gdkwayland.h>
 | 
				
			||||||
 | 
					#include <gtkmm.h>
 | 
				
			||||||
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <iterator>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "gtkmm/widget.h"
 | 
				
			||||||
 | 
					#include "modules/wlr/workspace_manager_binding.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace waybar::modules::wlr {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t                           WorkspaceGroup::workspace_global_id = 0;
 | 
				
			||||||
 | 
					uint32_t                           WorkspaceManager::group_global_id = 0;
 | 
				
			||||||
 | 
					std::map<std::string, std::string> Workspace::icons_map_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar,
 | 
				
			||||||
 | 
					                                   const Json::Value &config)
 | 
				
			||||||
 | 
					    : waybar::AModule(config, "workspaces", id, false, false),
 | 
				
			||||||
 | 
					      bar_(bar),
 | 
				
			||||||
 | 
					      box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
 | 
				
			||||||
 | 
					  auto config_sort_by_name = config_["sort-by-name"];
 | 
				
			||||||
 | 
					  if (config_sort_by_name.isBool()) {
 | 
				
			||||||
 | 
					    sort_by_name_ = config_sort_by_name.asBool();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto config_sort_by_coordinates = config_["sort-by-coordinates"];
 | 
				
			||||||
 | 
					  if (config_sort_by_coordinates.isBool()) {
 | 
				
			||||||
 | 
					    sort_by_coordinates_ = config_sort_by_coordinates.asBool();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto config_all_outputs = config_["all-outputs"];
 | 
				
			||||||
 | 
					  if (config_all_outputs.isBool()) {
 | 
				
			||||||
 | 
					    all_outputs_ = config_all_outputs.asBool();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto config_active_only = config_["active-only"];
 | 
				
			||||||
 | 
					  if (config_active_only.isBool()) {
 | 
				
			||||||
 | 
					    active_only_ = config_active_only.asBool();
 | 
				
			||||||
 | 
					    creation_delayed_ = active_only_;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  box_.set_name("workspaces");
 | 
				
			||||||
 | 
					  if (!id.empty()) {
 | 
				
			||||||
 | 
					    box_.get_style_context()->add_class(id);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  event_box_.add(box_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  add_registry_listener(this);
 | 
				
			||||||
 | 
					  if (!workspace_manager_) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceManager::workspace_comparator() const
 | 
				
			||||||
 | 
					    -> std::function<bool(std::unique_ptr<Workspace> &, std::unique_ptr<Workspace> &)> {
 | 
				
			||||||
 | 
					  return [=](std::unique_ptr<Workspace> &lhs, std::unique_ptr<Workspace> &rhs) {
 | 
				
			||||||
 | 
					    auto is_name_less = lhs->get_name() < rhs->get_name();
 | 
				
			||||||
 | 
					    auto is_name_eq = lhs->get_name() == rhs->get_name();
 | 
				
			||||||
 | 
					    auto is_coords_less = lhs->get_coords() < rhs->get_coords();
 | 
				
			||||||
 | 
					    if (sort_by_name_) {
 | 
				
			||||||
 | 
					      if (sort_by_coordinates_) {
 | 
				
			||||||
 | 
					        return is_name_eq ? is_coords_less : is_name_less;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return is_name_less;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (sort_by_coordinates_) {
 | 
				
			||||||
 | 
					      return is_coords_less;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return lhs->id() < rhs->id();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceManager::sort_workspaces() -> void {
 | 
				
			||||||
 | 
					  std::vector<std::reference_wrapper<std::unique_ptr<Workspace>>> all_workspaces;
 | 
				
			||||||
 | 
					  for (auto &group : groups_) {
 | 
				
			||||||
 | 
					    auto &group_workspaces = group->workspaces();
 | 
				
			||||||
 | 
					    all_workspaces.reserve(all_workspaces.size() +
 | 
				
			||||||
 | 
					                           std::distance(group_workspaces.begin(), group_workspaces.end()));
 | 
				
			||||||
 | 
					    if (!active_only()) {
 | 
				
			||||||
 | 
					      all_workspaces.insert(all_workspaces.end(), group_workspaces.begin(), group_workspaces.end());
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (auto &workspace : group_workspaces) {
 | 
				
			||||||
 | 
					      if (!workspace->is_active()) {
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      all_workspaces.push_back(workspace);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::sort(all_workspaces.begin(), all_workspaces.end(), workspace_comparator());
 | 
				
			||||||
 | 
					  for (size_t i = 0; i < all_workspaces.size(); ++i) {
 | 
				
			||||||
 | 
					    box_.reorder_child(all_workspaces[i].get()->get_button_ref(), i);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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_ = workspace_manager_bind(registry, name, version, this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceManager::handle_workspace_group_create(
 | 
				
			||||||
 | 
					    zext_workspace_group_handle_v1 *workspace_group_handle) -> void {
 | 
				
			||||||
 | 
					  auto new_id = ++group_global_id;
 | 
				
			||||||
 | 
					  groups_.push_back(
 | 
				
			||||||
 | 
					      std::make_unique<WorkspaceGroup>(bar_, box_, config_, *this, workspace_group_handle, new_id));
 | 
				
			||||||
 | 
					  spdlog::debug("Workspace group {} created", new_id);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceManager::handle_finished() -> void {
 | 
				
			||||||
 | 
					  zext_workspace_manager_v1_destroy(workspace_manager_);
 | 
				
			||||||
 | 
					  workspace_manager_ = nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceManager::handle_done() -> void {
 | 
				
			||||||
 | 
					  for (auto &group : groups_) {
 | 
				
			||||||
 | 
					    group->handle_done();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  dp.emit();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceManager::update() -> void {
 | 
				
			||||||
 | 
					  for (auto &group : groups_) {
 | 
				
			||||||
 | 
					    group->update();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (creation_delayed()) {
 | 
				
			||||||
 | 
					    creation_delayed_ = false;
 | 
				
			||||||
 | 
					    sort_workspaces();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  AModule::update();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WorkspaceManager::~WorkspaceManager() {
 | 
				
			||||||
 | 
					  if (!workspace_manager_) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  zext_workspace_manager_v1_destroy(workspace_manager_);
 | 
				
			||||||
 | 
					  workspace_manager_ = nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void {
 | 
				
			||||||
 | 
					  auto it = std::find_if(groups_.begin(),
 | 
				
			||||||
 | 
					                         groups_.end(),
 | 
				
			||||||
 | 
					                         [id](const std::unique_ptr<WorkspaceGroup> &g) { return g->id() == id; });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (it == groups_.end()) {
 | 
				
			||||||
 | 
					    spdlog::warn("Can't find group with id {}", id);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  groups_.erase(it);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					auto WorkspaceManager::commit() -> void { zext_workspace_manager_v1_commit(workspace_manager_); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value &config,
 | 
				
			||||||
 | 
					                               WorkspaceManager               &manager,
 | 
				
			||||||
 | 
					                               zext_workspace_group_handle_v1 *workspace_group_handle, uint32_t id)
 | 
				
			||||||
 | 
					    : bar_(bar),
 | 
				
			||||||
 | 
					      box_(box),
 | 
				
			||||||
 | 
					      config_(config),
 | 
				
			||||||
 | 
					      workspace_manager_(manager),
 | 
				
			||||||
 | 
					      workspace_group_handle_(workspace_group_handle),
 | 
				
			||||||
 | 
					      id_(id) {
 | 
				
			||||||
 | 
					  add_workspace_group_listener(workspace_group_handle, this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::active_only() const -> bool { return workspace_manager_.active_only(); }
 | 
				
			||||||
 | 
					auto WorkspaceGroup::creation_delayed() const -> bool {
 | 
				
			||||||
 | 
					  return workspace_manager_.creation_delayed();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::add_button(Gtk::Button &button) -> void {
 | 
				
			||||||
 | 
					  box_.pack_start(button, false, false);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WorkspaceGroup::~WorkspaceGroup() {
 | 
				
			||||||
 | 
					  if (!workspace_group_handle_) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  zext_workspace_group_handle_v1_destroy(workspace_group_handle_);
 | 
				
			||||||
 | 
					  workspace_group_handle_ = nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::handle_workspace_create(zext_workspace_handle_v1 *workspace) -> void {
 | 
				
			||||||
 | 
					  auto new_id = ++workspace_global_id;
 | 
				
			||||||
 | 
					  workspaces_.push_back(std::make_unique<Workspace>(bar_, config_, *this, workspace, new_id));
 | 
				
			||||||
 | 
					  spdlog::debug("Workspace {} created", new_id);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::handle_remove() -> void {
 | 
				
			||||||
 | 
					  zext_workspace_group_handle_v1_destroy(workspace_group_handle_);
 | 
				
			||||||
 | 
					  workspace_group_handle_ = nullptr;
 | 
				
			||||||
 | 
					  workspace_manager_.remove_workspace_group(id_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::handle_output_enter(wl_output *output) -> void {
 | 
				
			||||||
 | 
					  spdlog::debug("Output {} assigned to {} group", (void *)output, id_);
 | 
				
			||||||
 | 
					  output_ = output;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!is_visible() || workspace_manager_.creation_delayed()) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (auto &workspace : workspaces_) {
 | 
				
			||||||
 | 
					    add_button(workspace->get_button_ref());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::is_visible() const -> bool {
 | 
				
			||||||
 | 
					  return output_ != nullptr &&
 | 
				
			||||||
 | 
					         (workspace_manager_.all_outputs() ||
 | 
				
			||||||
 | 
					          output_ == gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::handle_output_leave() -> void {
 | 
				
			||||||
 | 
					  spdlog::debug("Output {} remove from {} group", (void *)output_, id_);
 | 
				
			||||||
 | 
					  output_ = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (output_ != gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj())) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (auto &workspace : workspaces_) {
 | 
				
			||||||
 | 
					    remove_button(workspace->get_button_ref());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::update() -> void {
 | 
				
			||||||
 | 
					  for (auto &workspace : workspaces_) {
 | 
				
			||||||
 | 
					    if (workspace_manager_.creation_delayed()) {
 | 
				
			||||||
 | 
					      add_button(workspace->get_button_ref());
 | 
				
			||||||
 | 
					      if (is_visible() && (workspace->is_active() || workspace->is_urgent())) {
 | 
				
			||||||
 | 
					        workspace->show();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    workspace->update();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::remove_workspace(uint32_t id) -> void {
 | 
				
			||||||
 | 
					  auto it = std::find_if(workspaces_.begin(),
 | 
				
			||||||
 | 
					                         workspaces_.end(),
 | 
				
			||||||
 | 
					                         [id](const std::unique_ptr<Workspace> &w) { return w->id() == id; });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (it == workspaces_.end()) {
 | 
				
			||||||
 | 
					    spdlog::warn("Can't find workspace with id {}", id);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  workspaces_.erase(it);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::handle_done() -> void {
 | 
				
			||||||
 | 
					  need_to_sort = false;
 | 
				
			||||||
 | 
					  if (!is_visible()) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (auto &workspace : workspaces_) {
 | 
				
			||||||
 | 
					    workspace->handle_done();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (creation_delayed()) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!workspace_manager_.all_outputs()) {
 | 
				
			||||||
 | 
					    sort_workspaces();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    workspace_manager_.sort_workspaces();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::commit() -> void { workspace_manager_.commit(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::sort_workspaces() -> void {
 | 
				
			||||||
 | 
					  std::sort(workspaces_.begin(), workspaces_.end(), workspace_manager_.workspace_comparator());
 | 
				
			||||||
 | 
					  for (size_t i = 0; i < workspaces_.size(); ++i) {
 | 
				
			||||||
 | 
					    box_.reorder_child(workspaces_[i]->get_button_ref(), i);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto WorkspaceGroup::remove_button(Gtk::Button &button) -> void { box_.remove(button); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group,
 | 
				
			||||||
 | 
					                     zext_workspace_handle_v1 *workspace, uint32_t id)
 | 
				
			||||||
 | 
					    : bar_(bar),
 | 
				
			||||||
 | 
					      config_(config),
 | 
				
			||||||
 | 
					      workspace_group_(workspace_group),
 | 
				
			||||||
 | 
					      workspace_handle_(workspace),
 | 
				
			||||||
 | 
					      id_(id) {
 | 
				
			||||||
 | 
					  add_workspace_listener(workspace, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto config_format = config["format"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  format_ = config_format.isString() ? config_format.asString() : "{name}";
 | 
				
			||||||
 | 
					  with_icon_ = format_.find("{icon}") != std::string::npos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (with_icon_ && icons_map_.empty()) {
 | 
				
			||||||
 | 
					    auto format_icons = config["format-icons"];
 | 
				
			||||||
 | 
					    for (auto &name : format_icons.getMemberNames()) {
 | 
				
			||||||
 | 
					      icons_map_.emplace(name, format_icons[name].asString());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Handle click events if configured */
 | 
				
			||||||
 | 
					  if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
 | 
				
			||||||
 | 
					      config_["on-click-right"].isString()) {
 | 
				
			||||||
 | 
					    button_.add_events(Gdk::BUTTON_PRESS_MASK);
 | 
				
			||||||
 | 
					    button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked),
 | 
				
			||||||
 | 
					                                                false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  button_.set_relief(Gtk::RELIEF_NONE);
 | 
				
			||||||
 | 
					  content_.set_center_widget(label_);
 | 
				
			||||||
 | 
					  button_.add(content_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!workspace_group.is_visible()) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  workspace_group.add_button(button_);
 | 
				
			||||||
 | 
					  button_.show_all();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Workspace::~Workspace() {
 | 
				
			||||||
 | 
					  workspace_group_.remove_button(button_);
 | 
				
			||||||
 | 
					  if (!workspace_handle_) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  zext_workspace_handle_v1_destroy(workspace_handle_);
 | 
				
			||||||
 | 
					  workspace_handle_ = nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Workspace::update() -> void {
 | 
				
			||||||
 | 
					  label_.set_markup(fmt::format(
 | 
				
			||||||
 | 
					      format_, fmt::arg("name", name_), fmt::arg("icon", with_icon_ ? get_icon() : "")));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Workspace::handle_state(const std::vector<uint32_t> &state) -> void {
 | 
				
			||||||
 | 
					  state_ = 0;
 | 
				
			||||||
 | 
					  for (auto state_entry : state) {
 | 
				
			||||||
 | 
					    switch (state_entry) {
 | 
				
			||||||
 | 
					      case ZEXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE:
 | 
				
			||||||
 | 
					        state_ |= (uint32_t)State::ACTIVE;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case ZEXT_WORKSPACE_HANDLE_V1_STATE_URGENT:
 | 
				
			||||||
 | 
					        state_ |= (uint32_t)State::URGENT;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case ZEXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN:
 | 
				
			||||||
 | 
					        state_ |= (uint32_t)State::HIDDEN;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Workspace::handle_remove() -> void {
 | 
				
			||||||
 | 
					  zext_workspace_handle_v1_destroy(workspace_handle_);
 | 
				
			||||||
 | 
					  workspace_handle_ = nullptr;
 | 
				
			||||||
 | 
					  workspace_group_.remove_workspace(id_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto add_or_remove_class(Glib::RefPtr<Gtk::StyleContext> context, bool condition,
 | 
				
			||||||
 | 
					                         const std::string &class_name) {
 | 
				
			||||||
 | 
					  if (condition) {
 | 
				
			||||||
 | 
					    context->add_class(class_name);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    context->remove_class(class_name);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Workspace::handle_done() -> void {
 | 
				
			||||||
 | 
					  spdlog::debug("Workspace {} changed to state {}", id_, state_);
 | 
				
			||||||
 | 
					  auto style_context = button_.get_style_context();
 | 
				
			||||||
 | 
					  add_or_remove_class(style_context, is_active(), "active");
 | 
				
			||||||
 | 
					  add_or_remove_class(style_context, is_urgent(), "urgent");
 | 
				
			||||||
 | 
					  add_or_remove_class(style_context, is_hidden(), "hidden");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (workspace_group_.creation_delayed()) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (workspace_group_.active_only() && (is_active() || is_urgent())) {
 | 
				
			||||||
 | 
					    button_.show_all();
 | 
				
			||||||
 | 
					  } else if (workspace_group_.active_only() && !(is_active() || is_urgent())) {
 | 
				
			||||||
 | 
					    button_.hide();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Workspace::get_icon() -> std::string {
 | 
				
			||||||
 | 
					  if (is_active()) {
 | 
				
			||||||
 | 
					    auto active_icon_it = icons_map_.find("active");
 | 
				
			||||||
 | 
					    if (active_icon_it != icons_map_.end()) {
 | 
				
			||||||
 | 
					      return active_icon_it->second;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto named_icon_it = icons_map_.find(name_);
 | 
				
			||||||
 | 
					  if (named_icon_it != icons_map_.end()) {
 | 
				
			||||||
 | 
					    return named_icon_it->second;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto default_icon_it = icons_map_.find("default");
 | 
				
			||||||
 | 
					  if (default_icon_it != icons_map_.end()) {
 | 
				
			||||||
 | 
					    return default_icon_it->second;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return name_;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Workspace::handle_clicked(GdkEventButton *bt) -> bool {
 | 
				
			||||||
 | 
					  std::string action;
 | 
				
			||||||
 | 
					  if (config_["on-click"].isString() && bt->button == 1) {
 | 
				
			||||||
 | 
					    action = config_["on-click"].asString();
 | 
				
			||||||
 | 
					  } else if (config_["on-click-middle"].isString() && bt->button == 2) {
 | 
				
			||||||
 | 
					    action = config_["on-click-middle"].asString();
 | 
				
			||||||
 | 
					  } else if (config_["on-click-right"].isString() && bt->button == 3) {
 | 
				
			||||||
 | 
					    action = config_["on-click-right"].asString();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (action.empty())
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  else if (action == "activate") {
 | 
				
			||||||
 | 
					    zext_workspace_handle_v1_activate(workspace_handle_);
 | 
				
			||||||
 | 
					  } else if (action == "close") {
 | 
				
			||||||
 | 
					    zext_workspace_handle_v1_remove(workspace_handle_);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    spdlog::warn("Unknown action {}", action);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  workspace_group_.commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Workspace::show() -> void { button_.show_all(); }
 | 
				
			||||||
 | 
					auto Workspace::hide() -> void { button_.hide(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Workspace::handle_name(const std::string &name) -> void {
 | 
				
			||||||
 | 
					  if (name_ != name) {
 | 
				
			||||||
 | 
					    workspace_group_.set_need_to_sort();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  name_ = name;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Workspace::handle_coordinates(const std::vector<uint32_t> &coordinates) -> void {
 | 
				
			||||||
 | 
					  if (coordinates_ != coordinates) {
 | 
				
			||||||
 | 
					    workspace_group_.set_need_to_sort();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  coordinates_ = coordinates;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					}  // namespace waybar::modules::wlr
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,135 @@
 | 
				
			||||||
 | 
					#include "modules/wlr/workspace_manager_binding.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "client.hpp"
 | 
				
			||||||
 | 
					#include "modules/wlr/workspace_manager.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, zext_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};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void add_registry_listener(void *data) {
 | 
				
			||||||
 | 
					  wl_display * display = Client::inst()->wl_display;
 | 
				
			||||||
 | 
					  wl_registry *registry = wl_display_get_registry(display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  wl_registry_add_listener(registry, ®istry_listener_impl, data);
 | 
				
			||||||
 | 
					  wl_display_roundtrip(display);
 | 
				
			||||||
 | 
					  wl_display_roundtrip(display);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void workspace_manager_handle_workspace_group(
 | 
				
			||||||
 | 
					    void *data, zext_workspace_manager_v1 *_, zext_workspace_group_handle_v1 *workspace_group) {
 | 
				
			||||||
 | 
					  static_cast<WorkspaceManager *>(data)->handle_workspace_group_create(workspace_group);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void workspace_manager_handle_done(void *data, zext_workspace_manager_v1 *_) {
 | 
				
			||||||
 | 
					  static_cast<WorkspaceManager *>(data)->handle_done();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void workspace_manager_handle_finished(void *data, zext_workspace_manager_v1 *_) {
 | 
				
			||||||
 | 
					  static_cast<WorkspaceManager *>(data)->handle_finished();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const zext_workspace_manager_v1_listener workspace_manager_impl = {
 | 
				
			||||||
 | 
					    .workspace_group = workspace_manager_handle_workspace_group,
 | 
				
			||||||
 | 
					    .done = workspace_manager_handle_done,
 | 
				
			||||||
 | 
					    .finished = workspace_manager_handle_finished,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					zext_workspace_manager_v1 *workspace_manager_bind(wl_registry *registry, uint32_t name,
 | 
				
			||||||
 | 
					                                                  uint32_t version, void *data) {
 | 
				
			||||||
 | 
					  auto *workspace_manager = static_cast<zext_workspace_manager_v1 *>(
 | 
				
			||||||
 | 
					      wl_registry_bind(registry, name, &zext_workspace_manager_v1_interface, version));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (workspace_manager)
 | 
				
			||||||
 | 
					    zext_workspace_manager_v1_add_listener(workspace_manager, &workspace_manager_impl, data);
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    spdlog::error("Failed to register manager");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return workspace_manager;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void workspace_group_handle_output_enter(void *data, zext_workspace_group_handle_v1 *_,
 | 
				
			||||||
 | 
					                                                wl_output *output) {
 | 
				
			||||||
 | 
					  static_cast<WorkspaceGroup *>(data)->handle_output_enter(output);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void workspace_group_handle_output_leave(void *data, zext_workspace_group_handle_v1 *_,
 | 
				
			||||||
 | 
					                                                wl_output *output) {
 | 
				
			||||||
 | 
					  static_cast<WorkspaceGroup *>(data)->handle_output_leave();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void workspace_group_handle_workspace(void *data, zext_workspace_group_handle_v1 *_,
 | 
				
			||||||
 | 
					                                             zext_workspace_handle_v1 *workspace) {
 | 
				
			||||||
 | 
					  static_cast<WorkspaceGroup *>(data)->handle_workspace_create(workspace);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void workspace_group_handle_remove(void *data, zext_workspace_group_handle_v1 *_) {
 | 
				
			||||||
 | 
					  static_cast<WorkspaceGroup *>(data)->handle_remove();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const zext_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};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void add_workspace_group_listener(zext_workspace_group_handle_v1 *workspace_group_handle,
 | 
				
			||||||
 | 
					                                  void *                          data) {
 | 
				
			||||||
 | 
					  zext_workspace_group_handle_v1_add_listener(workspace_group_handle, &workspace_group_impl, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void workspace_handle_name(void *data, struct zext_workspace_handle_v1 *_, const char *name) {
 | 
				
			||||||
 | 
					  static_cast<Workspace *>(data)->handle_name(name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void workspace_handle_coordinates(void *data, struct zext_workspace_handle_v1 *_,
 | 
				
			||||||
 | 
					                                  struct wl_array *coordinates) {
 | 
				
			||||||
 | 
					  std::vector<uint32_t> coords_vec;
 | 
				
			||||||
 | 
					  auto                  coords = static_cast<uint32_t *>(coordinates->data);
 | 
				
			||||||
 | 
					  for (size_t i = 0; i < coordinates->size / sizeof(uint32_t); ++i) {
 | 
				
			||||||
 | 
					    coords_vec.push_back(coords[i]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static_cast<Workspace *>(data)->handle_coordinates(coords_vec);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void workspace_handle_state(void *data, struct zext_workspace_handle_v1 *workspace_handle,
 | 
				
			||||||
 | 
					                            struct wl_array *state) {
 | 
				
			||||||
 | 
					  std::vector<uint32_t> state_vec;
 | 
				
			||||||
 | 
					  auto                  states = static_cast<uint32_t *>(state->data);
 | 
				
			||||||
 | 
					  for (size_t i = 0; i < state->size / sizeof(uint32_t); ++i) {
 | 
				
			||||||
 | 
					    state_vec.push_back(states[i]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static_cast<Workspace *>(data)->handle_state(state_vec);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void workspace_handle_remove(void *data, struct zext_workspace_handle_v1 *_) {
 | 
				
			||||||
 | 
					  static_cast<Workspace *>(data)->handle_remove();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const zext_workspace_handle_v1_listener workspace_impl = {
 | 
				
			||||||
 | 
					    .name = workspace_handle_name,
 | 
				
			||||||
 | 
					    .coordinates = workspace_handle_coordinates,
 | 
				
			||||||
 | 
					    .state = workspace_handle_state,
 | 
				
			||||||
 | 
					    .remove = workspace_handle_remove};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void add_workspace_listener(zext_workspace_handle_v1 *workspace_handle, void *data) {
 | 
				
			||||||
 | 
					  zext_workspace_handle_v1_add_listener(workspace_handle, &workspace_impl, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					}  // namespace waybar::modules::wlr
 | 
				
			||||||
		Loading…
	
		Reference in New Issue