2023-06-27 23:52:01 +00:00
|
|
|
#include "modules/hyprland/workspaces.hpp"
|
|
|
|
|
2023-06-30 21:18:57 +00:00
|
|
|
#include <json/value.h>
|
2023-06-27 23:52:01 +00:00
|
|
|
#include <spdlog/spdlog.h>
|
|
|
|
|
|
|
|
#include <algorithm>
|
2023-06-30 21:18:57 +00:00
|
|
|
#include <charconv>
|
2023-07-03 22:17:26 +00:00
|
|
|
#include <memory>
|
2023-06-27 23:52:01 +00:00
|
|
|
#include <string>
|
|
|
|
|
2023-10-09 16:53:00 +00:00
|
|
|
#include "util/regex_collection.hpp"
|
2023-09-22 21:18:02 +00:00
|
|
|
|
2023-06-27 23:52:01 +00:00
|
|
|
namespace waybar::modules::hyprland {
|
2023-06-30 21:18:57 +00:00
|
|
|
|
2023-10-09 17:42:53 +00:00
|
|
|
int Workspaces::window_rewrite_priority_function(std::string &window_rule) {
|
2023-10-09 16:53:00 +00:00
|
|
|
// Rules that match against title are prioritized
|
|
|
|
// Rules that don't specify if they're matching against either title or class are deprioritized
|
|
|
|
bool has_title = window_rule.find("title") != std::string::npos;
|
|
|
|
bool has_class = window_rule.find("class") != std::string::npos;
|
|
|
|
|
|
|
|
if (has_title && has_class) {
|
2023-10-09 17:42:53 +00:00
|
|
|
any_window_rewrite_rule_uses_title_ = true;
|
2023-10-09 16:53:00 +00:00
|
|
|
return 3;
|
|
|
|
} else if (has_title) {
|
2023-10-09 17:42:53 +00:00
|
|
|
any_window_rewrite_rule_uses_title_ = true;
|
2023-10-09 16:53:00 +00:00
|
|
|
return 2;
|
|
|
|
} else if (has_class) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-27 23:52:01 +00:00
|
|
|
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
|
|
|
|
: AModule(config, "workspaces", id, false, false),
|
|
|
|
bar_(bar),
|
|
|
|
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
|
2023-09-09 03:24:05 +00:00
|
|
|
parse_config(config);
|
|
|
|
|
|
|
|
box_.set_name("workspaces");
|
|
|
|
if (!id.empty()) {
|
|
|
|
box_.get_style_context()->add_class(id);
|
|
|
|
}
|
|
|
|
event_box_.add(box_);
|
|
|
|
|
|
|
|
register_ipc();
|
|
|
|
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto Workspaces::parse_config(const Json::Value &config) -> void {
|
2023-06-30 21:18:57 +00:00
|
|
|
Json::Value config_format = config["format"];
|
|
|
|
|
2023-08-02 21:02:50 +00:00
|
|
|
format_ = config_format.isString() ? config_format.asString() : "{name}";
|
2023-06-30 21:18:57 +00:00
|
|
|
with_icon_ = format_.find("{icon}") != std::string::npos;
|
|
|
|
|
|
|
|
if (with_icon_ && icons_map_.empty()) {
|
|
|
|
Json::Value format_icons = config["format-icons"];
|
|
|
|
for (std::string &name : format_icons.getMemberNames()) {
|
|
|
|
icons_map_.emplace(name, format_icons[name].asString());
|
|
|
|
}
|
|
|
|
|
|
|
|
icons_map_.emplace("", "");
|
|
|
|
}
|
|
|
|
|
2023-07-15 17:43:22 +00:00
|
|
|
auto config_all_outputs = config_["all-outputs"];
|
|
|
|
if (config_all_outputs.isBool()) {
|
|
|
|
all_outputs_ = config_all_outputs.asBool();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto config_show_special = config_["show-special"];
|
|
|
|
if (config_show_special.isBool()) {
|
|
|
|
show_special_ = config_show_special.asBool();
|
|
|
|
}
|
|
|
|
|
2023-08-10 17:40:14 +00:00
|
|
|
auto config_active_only = config_["active-only"];
|
|
|
|
if (config_active_only.isBool()) {
|
|
|
|
active_only_ = config_active_only.asBool();
|
|
|
|
}
|
|
|
|
|
2023-09-09 03:24:05 +00:00
|
|
|
auto config_sort_by = config_["sort-by"];
|
|
|
|
if (config_sort_by.isString()) {
|
2023-09-09 14:32:55 +00:00
|
|
|
auto sort_by_str = config_sort_by.asString();
|
|
|
|
try {
|
2023-09-09 17:02:56 +00:00
|
|
|
sort_by_ = enum_parser_.parseStringToEnum(sort_by_str, sort_map_);
|
2023-09-09 14:32:55 +00:00
|
|
|
} catch (const std::invalid_argument &e) {
|
|
|
|
// Handle the case where the string is not a valid enum representation.
|
2023-09-09 16:18:12 +00:00
|
|
|
sort_by_ = SORT_METHOD::DEFAULT;
|
2023-09-09 14:32:55 +00:00
|
|
|
g_warning("Invalid string representation for sort-by. Falling back to default sort method.");
|
|
|
|
}
|
2023-06-27 23:52:01 +00:00
|
|
|
}
|
2023-09-22 19:37:26 +00:00
|
|
|
|
|
|
|
Json::Value format_window_separator = config["format-window-separator"];
|
|
|
|
format_window_separator_ =
|
|
|
|
format_window_separator.isString() ? format_window_separator.asString() : " ";
|
|
|
|
|
2023-10-09 16:53:00 +00:00
|
|
|
Json::Value window_rewrite = config["window-rewrite"];
|
|
|
|
|
|
|
|
Json::Value window_rewrite_default_config = config["window-rewrite-default"];
|
|
|
|
std::string window_rewrite_default =
|
|
|
|
window_rewrite_default_config.isString() ? window_rewrite_default_config.asString() : "?";
|
2023-09-22 22:05:34 +00:00
|
|
|
|
2023-10-09 17:42:53 +00:00
|
|
|
window_rewrite_rules_ = util::RegexCollection(
|
|
|
|
window_rewrite, window_rewrite_default, [this](std::string &window_rule) {
|
|
|
|
return this->window_rewrite_priority_function(window_rule);
|
|
|
|
});
|
2023-09-09 03:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto Workspaces::register_ipc() -> void {
|
2023-06-27 23:52:01 +00:00
|
|
|
modulesReady = true;
|
2023-09-09 03:24:05 +00:00
|
|
|
|
2023-07-16 12:47:14 +00:00
|
|
|
if (!gIPC) {
|
2023-06-27 23:52:01 +00:00
|
|
|
gIPC = std::make_unique<IPC>();
|
|
|
|
}
|
|
|
|
|
2023-06-30 21:18:57 +00:00
|
|
|
gIPC->registerForIPC("workspace", this);
|
2023-06-27 23:52:01 +00:00
|
|
|
gIPC->registerForIPC("createworkspace", this);
|
|
|
|
gIPC->registerForIPC("destroyworkspace", this);
|
2023-07-15 17:43:22 +00:00
|
|
|
gIPC->registerForIPC("focusedmon", this);
|
|
|
|
gIPC->registerForIPC("moveworkspace", this);
|
2023-09-05 15:13:25 +00:00
|
|
|
gIPC->registerForIPC("renameworkspace", this);
|
2023-07-23 16:53:32 +00:00
|
|
|
gIPC->registerForIPC("openwindow", this);
|
|
|
|
gIPC->registerForIPC("closewindow", this);
|
|
|
|
gIPC->registerForIPC("movewindow", this);
|
2023-08-23 17:18:35 +00:00
|
|
|
gIPC->registerForIPC("urgent", this);
|
2023-10-09 17:42:53 +00:00
|
|
|
|
|
|
|
if (any_window_rewrite_rule_uses_title_) {
|
|
|
|
spdlog::info(
|
|
|
|
"Registering for Hyprland's 'windowtitle' events because a user-defined window "
|
|
|
|
"rewrite rule uses the 'title' field.");
|
|
|
|
gIPC->registerForIPC("windowtitle", this);
|
|
|
|
}
|
2023-06-27 23:52:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto Workspaces::update() -> void {
|
2023-07-15 17:43:22 +00:00
|
|
|
for (std::string workspace_to_remove : workspaces_to_remove_) {
|
2023-07-03 22:17:26 +00:00
|
|
|
remove_workspace(workspace_to_remove);
|
|
|
|
}
|
|
|
|
|
|
|
|
workspaces_to_remove_.clear();
|
|
|
|
|
2023-07-16 01:43:25 +00:00
|
|
|
for (Json::Value &workspace_to_create : workspaces_to_create_) {
|
2023-07-03 22:17:26 +00:00
|
|
|
create_workspace(workspace_to_create);
|
|
|
|
}
|
|
|
|
|
2023-07-16 01:43:25 +00:00
|
|
|
workspaces_to_create_.clear();
|
|
|
|
|
2023-08-10 17:40:14 +00:00
|
|
|
// get all active workspaces
|
|
|
|
auto monitors = gIPC->getSocket1JsonReply("monitors");
|
|
|
|
std::vector<std::string> visible_workspaces;
|
|
|
|
for (Json::Value &monitor : monitors) {
|
|
|
|
auto ws = monitor["activeWorkspace"];
|
|
|
|
if (ws.isObject() && (ws["name"].isString())) {
|
|
|
|
visible_workspaces.push_back(ws["name"].asString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-15 17:43:22 +00:00
|
|
|
for (auto &workspace : workspaces_) {
|
2023-08-10 17:40:14 +00:00
|
|
|
// active
|
2023-07-23 16:53:32 +00:00
|
|
|
workspace->set_active(workspace->name() == active_workspace_name_);
|
2023-08-10 17:40:14 +00:00
|
|
|
// disable urgency if workspace is active
|
|
|
|
if (workspace->name() == active_workspace_name_ && workspace->is_urgent()) {
|
2023-08-23 17:18:35 +00:00
|
|
|
workspace->set_urgent(false);
|
|
|
|
}
|
2023-08-10 17:40:14 +00:00
|
|
|
|
|
|
|
// visible
|
|
|
|
workspace->set_visible(std::find(visible_workspaces.begin(), visible_workspaces.end(),
|
|
|
|
workspace->name()) != visible_workspaces.end());
|
|
|
|
|
|
|
|
// set workspace icon
|
2023-06-30 21:18:57 +00:00
|
|
|
std::string &workspace_icon = icons_map_[""];
|
|
|
|
if (with_icon_) {
|
2023-07-03 22:17:26 +00:00
|
|
|
workspace_icon = workspace->select_icon(icons_map_);
|
2023-06-30 21:18:57 +00:00
|
|
|
}
|
2023-07-03 22:17:26 +00:00
|
|
|
workspace->update(format_, workspace_icon);
|
2023-06-27 23:52:01 +00:00
|
|
|
}
|
|
|
|
AModule::update();
|
|
|
|
}
|
|
|
|
|
2023-09-25 20:13:26 +00:00
|
|
|
bool isDoubleSpecial(std::string &workspace_name) {
|
|
|
|
// Hyprland's IPC sometimes reports the creation of workspaces strangely named
|
|
|
|
// `special:special:<some_name>`. This function checks for that and is used
|
|
|
|
// to avoid creating (and then removing) such workspaces.
|
|
|
|
// See hyprwm/Hyprland#3424 for more info.
|
|
|
|
return workspace_name.find("special:special:") != std::string::npos;
|
|
|
|
}
|
|
|
|
|
2023-06-30 21:18:57 +00:00
|
|
|
void Workspaces::onEvent(const std::string &ev) {
|
2023-07-03 22:17:26 +00:00
|
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
2023-06-30 21:18:57 +00:00
|
|
|
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>'));
|
|
|
|
std::string payload = ev.substr(eventName.size() + 2);
|
2023-07-15 17:43:22 +00:00
|
|
|
|
2023-06-30 21:18:57 +00:00
|
|
|
if (eventName == "workspace") {
|
2023-07-23 16:53:32 +00:00
|
|
|
active_workspace_name_ = payload;
|
2023-07-15 17:43:22 +00:00
|
|
|
|
2023-06-30 21:18:57 +00:00
|
|
|
} else if (eventName == "destroyworkspace") {
|
2023-09-25 20:13:26 +00:00
|
|
|
if (!isDoubleSpecial(payload)) {
|
|
|
|
workspaces_to_remove_.push_back(payload);
|
|
|
|
}
|
2023-06-30 21:18:57 +00:00
|
|
|
} else if (eventName == "createworkspace") {
|
2023-07-15 17:43:22 +00:00
|
|
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
2023-07-16 01:43:25 +00:00
|
|
|
for (Json::Value workspace_json : workspaces_json) {
|
2023-08-10 17:40:14 +00:00
|
|
|
std::string name = workspace_json["name"].asString();
|
|
|
|
if (name == payload &&
|
2023-07-15 17:43:22 +00:00
|
|
|
(all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
2023-09-25 20:13:26 +00:00
|
|
|
(show_special() || !name.starts_with("special")) && !isDoubleSpecial(payload)) {
|
2023-07-16 01:49:46 +00:00
|
|
|
workspaces_to_create_.push_back(workspace_json);
|
|
|
|
break;
|
|
|
|
}
|
2023-07-15 17:43:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} else if (eventName == "focusedmon") {
|
2023-07-23 16:53:32 +00:00
|
|
|
active_workspace_name_ = payload.substr(payload.find(',') + 1);
|
2023-07-15 17:43:22 +00:00
|
|
|
|
2023-07-16 01:18:41 +00:00
|
|
|
} else if (eventName == "moveworkspace" && !all_outputs()) {
|
2023-07-16 12:47:14 +00:00
|
|
|
std::string workspace = payload.substr(0, payload.find(','));
|
|
|
|
std::string new_output = payload.substr(payload.find(',') + 1);
|
2023-07-16 01:18:41 +00:00
|
|
|
if (bar_.output->name == new_output) { // TODO: implement this better
|
|
|
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
2023-07-16 01:43:25 +00:00
|
|
|
for (Json::Value workspace_json : workspaces_json) {
|
2023-08-10 17:40:14 +00:00
|
|
|
std::string name = workspace_json["name"].asString();
|
|
|
|
if (name == workspace && bar_.output->name == workspace_json["monitor"].asString()) {
|
2023-07-16 01:43:25 +00:00
|
|
|
workspaces_to_create_.push_back(workspace_json);
|
2023-07-16 01:18:41 +00:00
|
|
|
break;
|
2023-07-15 17:43:22 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-16 01:18:41 +00:00
|
|
|
} else {
|
|
|
|
workspaces_to_remove_.push_back(workspace);
|
2023-07-15 17:43:22 +00:00
|
|
|
}
|
2023-09-22 19:37:26 +00:00
|
|
|
} else if (eventName == "openwindow") {
|
2023-07-23 16:53:32 +00:00
|
|
|
update_window_count();
|
2023-09-22 19:37:26 +00:00
|
|
|
on_window_opened(payload);
|
|
|
|
} else if (eventName == "closewindow") {
|
|
|
|
update_window_count();
|
|
|
|
on_window_closed(payload);
|
|
|
|
} else if (eventName == "movewindow") {
|
2023-07-23 16:53:32 +00:00
|
|
|
update_window_count();
|
2023-09-22 19:37:26 +00:00
|
|
|
on_window_moved(payload);
|
2023-08-23 17:18:35 +00:00
|
|
|
} else if (eventName == "urgent") {
|
|
|
|
set_urgent_workspace(payload);
|
2023-09-05 15:13:25 +00:00
|
|
|
} else if (eventName == "renameworkspace") {
|
|
|
|
std::string workspace_id_str = payload.substr(0, payload.find(','));
|
|
|
|
int workspace_id = workspace_id_str == "special" ? -99 : std::stoi(workspace_id_str);
|
|
|
|
std::string new_name = payload.substr(payload.find(',') + 1);
|
|
|
|
for (auto &workspace : workspaces_) {
|
|
|
|
if (workspace->id() == workspace_id) {
|
2023-09-08 19:11:02 +00:00
|
|
|
if (workspace->name() == active_workspace_name_) {
|
|
|
|
active_workspace_name_ = new_name;
|
|
|
|
}
|
2023-09-05 15:13:25 +00:00
|
|
|
workspace->set_name(new_name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-10-09 17:42:53 +00:00
|
|
|
} else if (eventName == "windowtitle") {
|
|
|
|
auto window_workspace =
|
|
|
|
std::find_if(workspaces_.begin(), workspaces_.end(),
|
|
|
|
[payload](auto &workspace) { return workspace->contains_window(payload); });
|
|
|
|
|
|
|
|
if (window_workspace != workspaces_.end()) {
|
|
|
|
Json::Value clients_data = gIPC->getSocket1JsonReply("clients");
|
|
|
|
std::string json_window_address = fmt::format("0x{}", payload);
|
|
|
|
|
|
|
|
auto client = std::find_if(clients_data.begin(), clients_data.end(),
|
|
|
|
[json_window_address](auto &client) {
|
|
|
|
return client["address"].asString() == json_window_address;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!client->empty()) {
|
|
|
|
(*window_workspace)
|
|
|
|
->insert_window(payload, (*client)["class"].asString(), (*client)["title"].asString());
|
|
|
|
}
|
|
|
|
}
|
2023-06-30 21:18:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dp.emit();
|
|
|
|
}
|
2023-06-27 23:52:01 +00:00
|
|
|
|
2023-09-22 19:37:26 +00:00
|
|
|
void Workspaces::on_window_opened(std::string payload) {
|
|
|
|
size_t last_comma_idx = 0;
|
|
|
|
size_t next_comma_idx = payload.find(',');
|
|
|
|
std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx);
|
|
|
|
|
|
|
|
last_comma_idx = next_comma_idx;
|
|
|
|
next_comma_idx = payload.find(',', next_comma_idx + 1);
|
|
|
|
std::string workspace_name =
|
|
|
|
payload.substr(last_comma_idx + 1, next_comma_idx - last_comma_idx - 1);
|
|
|
|
|
|
|
|
last_comma_idx = next_comma_idx;
|
|
|
|
next_comma_idx = payload.find(',', next_comma_idx + 1);
|
|
|
|
std::string window_class =
|
|
|
|
payload.substr(last_comma_idx + 1, next_comma_idx - last_comma_idx - 1);
|
|
|
|
|
|
|
|
std::string window_title = payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx);
|
|
|
|
|
|
|
|
for (auto &workspace : workspaces_) {
|
|
|
|
if (workspace->on_window_opened(window_address, workspace_name, window_class, window_title)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Workspaces::on_window_closed(std::string addr) {
|
|
|
|
for (auto &workspace : workspaces_) {
|
|
|
|
if (workspace->on_window_closed(addr)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Workspaces::on_window_moved(std::string payload) {
|
|
|
|
size_t last_comma_idx = 0;
|
|
|
|
size_t next_comma_idx = payload.find(',');
|
|
|
|
std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx);
|
|
|
|
|
|
|
|
std::string workspace_name =
|
|
|
|
payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx);
|
|
|
|
|
2023-09-23 00:12:42 +00:00
|
|
|
std::string window_repr;
|
2023-09-22 19:37:26 +00:00
|
|
|
|
2023-09-23 00:12:42 +00:00
|
|
|
// Take the window's representation from the old workspace...
|
|
|
|
for (auto &workspace : workspaces_) {
|
|
|
|
try {
|
|
|
|
window_repr = workspace->on_window_closed(window_address).value();
|
|
|
|
break;
|
|
|
|
} catch (const std::bad_optional_access &e) {
|
|
|
|
// window was not found in this workspace
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2023-09-22 19:37:26 +00:00
|
|
|
|
2023-09-23 00:12:42 +00:00
|
|
|
// ...and add it to the new workspace
|
2023-09-22 19:37:26 +00:00
|
|
|
for (auto &workspace : workspaces_) {
|
2023-09-23 00:12:42 +00:00
|
|
|
if (workspace->on_window_opened(window_address, workspace_name, window_repr)) {
|
|
|
|
break;
|
2023-09-22 19:37:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-23 16:53:32 +00:00
|
|
|
void Workspaces::update_window_count() {
|
|
|
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
|
|
|
for (auto &workspace : workspaces_) {
|
|
|
|
auto workspace_json = std::find_if(
|
|
|
|
workspaces_json.begin(), workspaces_json.end(),
|
|
|
|
[&](Json::Value const &x) { return x["name"].asString() == workspace->name(); });
|
2023-08-10 17:40:14 +00:00
|
|
|
uint32_t count = 0;
|
2023-07-23 16:53:32 +00:00
|
|
|
if (workspace_json != workspaces_json.end()) {
|
|
|
|
try {
|
2023-08-10 17:40:14 +00:00
|
|
|
count = (*workspace_json)["windows"].asUInt();
|
2023-07-23 16:53:32 +00:00
|
|
|
} catch (const std::exception &e) {
|
|
|
|
spdlog::error("Failed to update window count: {}", e.what());
|
|
|
|
}
|
|
|
|
}
|
2023-08-10 17:40:14 +00:00
|
|
|
workspace->set_windows(count);
|
2023-07-23 16:53:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-22 19:37:26 +00:00
|
|
|
void Workspaces::initialize_window_maps() {
|
|
|
|
Json::Value clients_data = gIPC->getSocket1JsonReply("clients");
|
|
|
|
for (auto &workspace : workspaces_) {
|
|
|
|
workspace->initialize_window_map(clients_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Workspace::initialize_window_map(const Json::Value &clients_data) {
|
|
|
|
window_map_.clear();
|
|
|
|
for (auto client : clients_data) {
|
|
|
|
if (client["workspace"]["id"].asInt() == id()) {
|
2023-09-22 21:38:18 +00:00
|
|
|
// substr(2, ...) is necessary because Hyprland's JSON follows this format:
|
|
|
|
// 0x{ADDR}
|
|
|
|
// While Hyprland's IPC follows this format:
|
|
|
|
// {ADDR}
|
2023-09-22 19:37:26 +00:00
|
|
|
WindowAddress client_address = client["address"].asString();
|
2023-09-22 21:38:18 +00:00
|
|
|
client_address = client_address.substr(2, client_address.length() - 2);
|
2023-10-09 16:53:00 +00:00
|
|
|
insert_window(client_address, client["class"].asString(), client["title"].asString());
|
2023-09-22 19:37:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-09 16:53:00 +00:00
|
|
|
void Workspace::insert_window(WindowAddress addr, std::string window_class,
|
|
|
|
std::string window_title) {
|
|
|
|
auto window_repr = workspace_manager_.get_rewrite(window_class, window_title);
|
2023-09-22 21:38:18 +00:00
|
|
|
if (!window_repr.empty()) {
|
2023-10-09 17:42:53 +00:00
|
|
|
window_map_[addr] = window_repr;
|
2023-09-22 21:38:18 +00:00
|
|
|
}
|
2023-09-22 21:18:02 +00:00
|
|
|
};
|
|
|
|
|
2023-09-23 00:12:42 +00:00
|
|
|
std::string Workspace::remove_window(WindowAddress addr) {
|
|
|
|
std::string window_repr = window_map_[addr];
|
|
|
|
window_map_.erase(addr);
|
|
|
|
|
|
|
|
return window_repr;
|
|
|
|
}
|
|
|
|
|
2023-09-22 19:37:26 +00:00
|
|
|
bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name,
|
2023-09-23 00:12:42 +00:00
|
|
|
std::string window_repr) {
|
2023-09-22 19:37:26 +00:00
|
|
|
if (workspace_name == name()) {
|
2023-10-09 17:42:53 +00:00
|
|
|
window_map_[addr] = window_repr;
|
2023-09-23 00:12:42 +00:00
|
|
|
return true;
|
2023-09-22 19:37:26 +00:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name,
|
|
|
|
std::string &window_class, std::string &window_title) {
|
|
|
|
if (workspace_name == name()) {
|
2023-10-09 16:53:00 +00:00
|
|
|
insert_window(addr, window_class, window_title);
|
2023-09-22 19:37:26 +00:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-23 00:12:42 +00:00
|
|
|
std::optional<std::string> Workspace::on_window_closed(WindowAddress &addr) {
|
2023-09-22 19:37:26 +00:00
|
|
|
if (window_map_.contains(addr)) {
|
2023-09-23 00:12:42 +00:00
|
|
|
return remove_window(addr);
|
2023-09-22 19:37:26 +00:00
|
|
|
} else {
|
2023-09-23 00:12:42 +00:00
|
|
|
return {};
|
2023-09-22 19:37:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Workspaces::create_workspace(Json::Value &workspace_data, const Json::Value &clients_data) {
|
2023-07-23 16:53:32 +00:00
|
|
|
// replace the existing persistent workspace if it exists
|
|
|
|
auto workspace = std::find_if(
|
|
|
|
workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr<Workspace> const &x) {
|
2023-09-22 19:37:26 +00:00
|
|
|
auto name = workspace_data["name"].asString();
|
2023-07-23 16:53:32 +00:00
|
|
|
return x->is_persistent() &&
|
|
|
|
((name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name());
|
|
|
|
});
|
|
|
|
if (workspace != workspaces_.end()) {
|
|
|
|
// replace workspace, but keep persistent flag
|
|
|
|
workspaces_.erase(workspace);
|
2023-09-22 19:37:26 +00:00
|
|
|
workspace_data["persistent"] = true;
|
2023-07-23 16:53:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// create new workspace
|
2023-09-22 19:37:26 +00:00
|
|
|
workspaces_.emplace_back(std::make_unique<Workspace>(workspace_data, *this, clients_data));
|
2023-07-03 22:17:26 +00:00
|
|
|
Gtk::Button &new_workspace_button = workspaces_.back()->button();
|
|
|
|
box_.pack_start(new_workspace_button, false, false);
|
|
|
|
sort_workspaces();
|
|
|
|
new_workspace_button.show_all();
|
|
|
|
}
|
|
|
|
|
2023-07-15 17:43:22 +00:00
|
|
|
void Workspaces::remove_workspace(std::string name) {
|
2023-09-25 20:12:51 +00:00
|
|
|
auto workspace =
|
|
|
|
std::find_if(workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr<Workspace> &x) {
|
|
|
|
return (name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name();
|
|
|
|
});
|
2023-07-03 22:17:26 +00:00
|
|
|
|
2023-07-03 22:24:34 +00:00
|
|
|
if (workspace == workspaces_.end()) {
|
2023-07-23 16:53:32 +00:00
|
|
|
// happens when a workspace on another monitor is destroyed
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*workspace)->is_persistent()) {
|
|
|
|
// don't remove persistent workspaces, create_workspace will take care of replacement
|
2023-07-03 22:24:34 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-07-03 22:17:26 +00:00
|
|
|
|
2023-07-03 22:24:34 +00:00
|
|
|
box_.remove(workspace->get()->button());
|
|
|
|
workspaces_.erase(workspace);
|
2023-07-03 22:17:26 +00:00
|
|
|
}
|
|
|
|
|
2023-07-23 16:53:32 +00:00
|
|
|
void Workspaces::fill_persistent_workspaces() {
|
2023-08-10 17:40:14 +00:00
|
|
|
if (config_["persistent_workspaces"].isObject()) {
|
2023-09-05 18:45:09 +00:00
|
|
|
spdlog::warn(
|
|
|
|
"persistent_workspaces is deprecated. Please change config to use persistent-workspaces.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config_["persistent-workspaces"].isObject() || config_["persistent_workspaces"].isObject()) {
|
|
|
|
const Json::Value persistent_workspaces = config_["persistent-workspaces"].isObject()
|
|
|
|
? config_["persistent-workspaces"]
|
|
|
|
: config_["persistent_workspaces"];
|
2023-07-23 16:53:32 +00:00
|
|
|
const std::vector<std::string> keys = persistent_workspaces.getMemberNames();
|
|
|
|
|
|
|
|
for (const std::string &key : keys) {
|
2023-08-29 04:34:28 +00:00
|
|
|
// only add if either:
|
|
|
|
// 1. key is "*" and this monitor is not already defined in the config
|
|
|
|
// 2. key is the current monitor name
|
|
|
|
bool can_create =
|
|
|
|
(key == "*" && std::find(keys.begin(), keys.end(), bar_.output->name) == keys.end()) ||
|
|
|
|
key == bar_.output->name;
|
2023-07-23 16:53:32 +00:00
|
|
|
const Json::Value &value = persistent_workspaces[key];
|
2023-08-29 04:34:28 +00:00
|
|
|
|
2023-07-23 16:53:32 +00:00
|
|
|
if (value.isInt()) {
|
|
|
|
// value is a number => create that many workspaces for this monitor
|
2023-08-29 04:34:28 +00:00
|
|
|
if (can_create) {
|
2023-07-23 16:53:32 +00:00
|
|
|
int amount = value.asInt();
|
|
|
|
spdlog::debug("Creating {} persistent workspaces for monitor {}", amount,
|
|
|
|
bar_.output->name);
|
|
|
|
for (int i = 0; i < amount; i++) {
|
|
|
|
persistent_workspaces_to_create_.emplace_back(
|
|
|
|
std::to_string(monitor_id_ * amount + i + 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (value.isArray() && !value.empty()) {
|
2023-08-29 04:34:28 +00:00
|
|
|
// value is an array => create defined workspaces for this monitor
|
|
|
|
if (can_create) {
|
|
|
|
for (const Json::Value &workspace : value) {
|
|
|
|
if (workspace.isInt()) {
|
|
|
|
spdlog::debug("Creating workspace {} on monitor {}", workspace, bar_.output->name);
|
|
|
|
persistent_workspaces_to_create_.emplace_back(std::to_string(workspace.asInt()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// key is the workspace and value is array of monitors to create on
|
|
|
|
for (const Json::Value &monitor : value) {
|
|
|
|
if (monitor.isString() && monitor.asString() == bar_.output->name) {
|
|
|
|
persistent_workspaces_to_create_.emplace_back(key);
|
|
|
|
break;
|
|
|
|
}
|
2023-07-23 16:53:32 +00:00
|
|
|
}
|
|
|
|
}
|
2023-08-30 11:35:19 +00:00
|
|
|
} else {
|
|
|
|
// this workspace should be displayed on all monitors
|
|
|
|
persistent_workspaces_to_create_.emplace_back(key);
|
2023-07-23 16:53:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Workspaces::create_persistent_workspaces() {
|
|
|
|
for (const std::string &workspace_name : persistent_workspaces_to_create_) {
|
|
|
|
Json::Value new_workspace;
|
|
|
|
try {
|
|
|
|
// numbered persistent workspaces get the name as ID
|
|
|
|
new_workspace["id"] = workspace_name == "special" ? -99 : std::stoi(workspace_name);
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
// named persistent workspaces start with ID=0
|
|
|
|
new_workspace["id"] = 0;
|
|
|
|
}
|
|
|
|
new_workspace["name"] = workspace_name;
|
|
|
|
new_workspace["monitor"] = bar_.output->name;
|
|
|
|
new_workspace["windows"] = 0;
|
|
|
|
new_workspace["persistent"] = true;
|
|
|
|
|
|
|
|
create_workspace(new_workspace);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-27 23:52:01 +00:00
|
|
|
void Workspaces::init() {
|
2023-07-23 16:53:32 +00:00
|
|
|
active_workspace_name_ = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString();
|
|
|
|
|
|
|
|
// get monitor ID from name (used by persistent workspaces)
|
|
|
|
monitor_id_ = 0;
|
|
|
|
auto monitors = gIPC->getSocket1JsonReply("monitors");
|
|
|
|
auto current_monitor = std::find_if(
|
|
|
|
monitors.begin(), monitors.end(),
|
|
|
|
[this](const Json::Value &m) { return m["name"].asString() == bar_.output->name; });
|
|
|
|
if (current_monitor == monitors.end()) {
|
|
|
|
spdlog::error("Monitor '{}' does not have an ID? Using 0", bar_.output->name);
|
|
|
|
} else {
|
|
|
|
monitor_id_ = (*current_monitor)["id"].asInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
fill_persistent_workspaces();
|
|
|
|
create_persistent_workspaces();
|
2023-07-15 17:43:22 +00:00
|
|
|
|
2023-06-27 23:52:01 +00:00
|
|
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
2023-09-22 19:37:26 +00:00
|
|
|
const Json::Value clients_json = gIPC->getSocket1JsonReply("clients");
|
|
|
|
|
2023-07-16 01:43:25 +00:00
|
|
|
for (Json::Value workspace_json : workspaces_json) {
|
2023-07-15 17:43:22 +00:00
|
|
|
if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
2023-08-10 17:40:14 +00:00
|
|
|
(!workspace_json["name"].asString().starts_with("special") || show_special())) {
|
2023-09-22 19:37:26 +00:00
|
|
|
create_workspace(workspace_json, clients_json);
|
2023-08-10 17:40:14 +00:00
|
|
|
}
|
2023-06-27 23:52:01 +00:00
|
|
|
}
|
|
|
|
|
2023-07-23 16:53:32 +00:00
|
|
|
update_window_count();
|
|
|
|
|
2023-06-30 21:18:57 +00:00
|
|
|
sort_workspaces();
|
|
|
|
|
2023-06-27 23:52:01 +00:00
|
|
|
dp.emit();
|
|
|
|
}
|
|
|
|
|
|
|
|
Workspaces::~Workspaces() {
|
|
|
|
gIPC->unregisterForIPC(this);
|
|
|
|
// wait for possible event handler to finish
|
|
|
|
std::lock_guard<std::mutex> lg(mutex_);
|
|
|
|
}
|
|
|
|
|
2023-09-22 19:37:26 +00:00
|
|
|
Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager,
|
|
|
|
const Json::Value &clients_data)
|
2023-08-10 17:40:14 +00:00
|
|
|
: workspace_manager_(workspace_manager),
|
|
|
|
id_(workspace_data["id"].asInt()),
|
2023-07-16 12:47:14 +00:00
|
|
|
name_(workspace_data["name"].asString()),
|
|
|
|
output_(workspace_data["monitor"].asString()), // TODO:allow using monitor desc
|
2023-07-23 16:53:32 +00:00
|
|
|
windows_(workspace_data["windows"].asInt()),
|
|
|
|
active_(true) {
|
|
|
|
if (name_.starts_with("name:")) {
|
2023-07-15 17:43:22 +00:00
|
|
|
name_ = name_.substr(5);
|
2023-07-23 16:53:32 +00:00
|
|
|
} else if (name_.starts_with("special")) {
|
|
|
|
name_ = id_ == -99 ? name_ : name_.substr(8);
|
2023-07-16 12:47:14 +00:00
|
|
|
is_special_ = true;
|
2023-07-15 17:43:22 +00:00
|
|
|
}
|
2023-06-27 23:52:01 +00:00
|
|
|
|
2023-07-23 16:53:32 +00:00
|
|
|
if (workspace_data.isMember("persistent")) {
|
|
|
|
is_persistent_ = workspace_data["persistent"].asBool();
|
|
|
|
}
|
|
|
|
|
2023-07-15 17:43:22 +00:00
|
|
|
button_.add_events(Gdk::BUTTON_PRESS_MASK);
|
|
|
|
button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked),
|
|
|
|
false);
|
2023-06-30 21:18:57 +00:00
|
|
|
|
2023-06-27 23:52:01 +00:00
|
|
|
button_.set_relief(Gtk::RELIEF_NONE);
|
|
|
|
content_.set_center_widget(label_);
|
|
|
|
button_.add(content_);
|
2023-09-22 19:37:26 +00:00
|
|
|
|
|
|
|
initialize_window_map(clients_data);
|
2023-09-03 04:34:11 +00:00
|
|
|
}
|
2023-06-27 23:52:01 +00:00
|
|
|
|
2023-07-16 12:47:14 +00:00
|
|
|
void add_or_remove_class(const Glib::RefPtr<Gtk::StyleContext> &context, bool condition,
|
2023-06-30 21:18:57 +00:00
|
|
|
const std::string &class_name) {
|
|
|
|
if (condition) {
|
|
|
|
context->add_class(class_name);
|
|
|
|
} else {
|
|
|
|
context->remove_class(class_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Workspace::update(const std::string &format, const std::string &icon) {
|
2023-08-10 17:40:14 +00:00
|
|
|
// clang-format off
|
|
|
|
if (this->workspace_manager_.active_only() && \
|
|
|
|
!this->active() && \
|
|
|
|
!this->is_persistent() && \
|
|
|
|
!this->is_visible() && \
|
|
|
|
!this->is_special()) {
|
|
|
|
// clang-format on
|
|
|
|
// if active_only is true, hide if not active, persistent, visible or special
|
|
|
|
button_.hide();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
button_.show();
|
|
|
|
|
2023-07-16 12:47:14 +00:00
|
|
|
auto style_context = button_.get_style_context();
|
2023-06-30 21:18:57 +00:00
|
|
|
add_or_remove_class(style_context, active(), "active");
|
2023-07-23 16:53:32 +00:00
|
|
|
add_or_remove_class(style_context, is_special(), "special");
|
2023-08-24 19:19:20 +00:00
|
|
|
add_or_remove_class(style_context, is_empty(), "empty");
|
|
|
|
add_or_remove_class(style_context, is_persistent(), "persistent");
|
2023-08-23 17:18:35 +00:00
|
|
|
add_or_remove_class(style_context, is_urgent(), "urgent");
|
2023-08-10 17:40:14 +00:00
|
|
|
add_or_remove_class(style_context, is_visible(), "visible");
|
2023-06-30 21:18:57 +00:00
|
|
|
|
2023-09-22 21:18:02 +00:00
|
|
|
std::string windows;
|
|
|
|
auto window_separator = workspace_manager_.get_window_separator();
|
|
|
|
|
|
|
|
bool is_not_first = false;
|
2023-09-22 19:37:26 +00:00
|
|
|
|
|
|
|
for (auto &[_pid, window_repr] : window_map_) {
|
2023-09-22 21:18:02 +00:00
|
|
|
if (is_not_first) {
|
|
|
|
windows.append(window_separator);
|
|
|
|
}
|
|
|
|
is_not_first = true;
|
|
|
|
windows.append(window_repr);
|
2023-09-22 19:37:26 +00:00
|
|
|
}
|
|
|
|
|
2023-08-10 18:37:42 +00:00
|
|
|
label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
|
2023-09-22 19:37:26 +00:00
|
|
|
fmt::arg("name", name()), fmt::arg("icon", icon),
|
2023-09-22 21:18:02 +00:00
|
|
|
fmt::arg("windows", windows)));
|
2023-06-30 21:18:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Workspaces::sort_workspaces() {
|
|
|
|
std::sort(workspaces_.begin(), workspaces_.end(),
|
2023-09-09 03:24:28 +00:00
|
|
|
[&](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
|
|
|
|
// Helper comparisons
|
|
|
|
auto is_id_less = a->id() < b->id();
|
|
|
|
auto is_name_less = a->name() < b->name();
|
|
|
|
|
2023-09-09 14:32:55 +00:00
|
|
|
switch (sort_by_) {
|
2023-09-09 16:18:12 +00:00
|
|
|
case SORT_METHOD::ID:
|
2023-09-09 03:24:28 +00:00
|
|
|
return is_id_less;
|
2023-09-09 16:18:12 +00:00
|
|
|
case SORT_METHOD::NAME:
|
2023-09-09 03:24:28 +00:00
|
|
|
return is_name_less;
|
2023-09-09 16:18:12 +00:00
|
|
|
case SORT_METHOD::NUMBER:
|
2023-09-09 14:32:55 +00:00
|
|
|
try {
|
2023-09-13 16:20:13 +00:00
|
|
|
return std::stoi(a->name()) < std::stoi(b->name());
|
2023-09-09 14:32:55 +00:00
|
|
|
} catch (const std::invalid_argument &) {
|
|
|
|
// Handle the exception if necessary.
|
|
|
|
break;
|
|
|
|
}
|
2023-09-09 16:18:12 +00:00
|
|
|
case SORT_METHOD::DEFAULT:
|
2023-09-09 14:32:55 +00:00
|
|
|
default:
|
|
|
|
// Handle the default case here.
|
|
|
|
// normal -> named persistent -> named -> special -> named special
|
|
|
|
|
|
|
|
// both normal (includes numbered persistent) => sort by ID
|
|
|
|
if (a->id() > 0 && b->id() > 0) {
|
|
|
|
return is_id_less;
|
|
|
|
}
|
|
|
|
|
|
|
|
// one normal, one special => normal first
|
|
|
|
if ((a->is_special()) ^ (b->is_special())) {
|
|
|
|
return b->is_special();
|
|
|
|
}
|
2023-07-23 16:53:32 +00:00
|
|
|
|
2023-09-09 14:32:55 +00:00
|
|
|
// only one normal, one named
|
|
|
|
if ((a->id() > 0) ^ (b->id() > 0)) {
|
|
|
|
return a->id() > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// both special
|
|
|
|
if (a->is_special() && b->is_special()) {
|
|
|
|
// if one is -99 => put it last
|
|
|
|
if (a->id() == -99 || b->id() == -99) {
|
|
|
|
return b->id() == -99;
|
|
|
|
}
|
|
|
|
// both are 0 (not yet named persistents) / both are named specials (-98 <= ID
|
|
|
|
// <=-1)
|
|
|
|
return is_name_less;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort non-special named workspaces by name (ID <= -1377)
|
|
|
|
return is_name_less;
|
|
|
|
break;
|
2023-09-09 03:24:28 +00:00
|
|
|
}
|
2023-09-09 14:32:55 +00:00
|
|
|
|
|
|
|
// Return a default value if none of the cases match.
|
|
|
|
return is_name_less; // You can adjust this to your specific needs.
|
2023-07-03 22:17:26 +00:00
|
|
|
});
|
2023-06-30 21:18:57 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < workspaces_.size(); ++i) {
|
2023-07-03 22:17:26 +00:00
|
|
|
box_.reorder_child(workspaces_[i]->button(), i);
|
2023-06-30 21:18:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string &Workspace::select_icon(std::map<std::string, std::string> &icons_map) {
|
2023-09-07 17:33:35 +00:00
|
|
|
if (is_urgent()) {
|
|
|
|
auto urgent_icon_it = icons_map.find("urgent");
|
|
|
|
if (urgent_icon_it != icons_map.end()) {
|
|
|
|
return urgent_icon_it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-30 21:18:57 +00:00
|
|
|
if (active()) {
|
|
|
|
auto active_icon_it = icons_map.find("active");
|
|
|
|
if (active_icon_it != icons_map.end()) {
|
|
|
|
return active_icon_it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-23 16:53:32 +00:00
|
|
|
if (is_special()) {
|
|
|
|
auto special_icon_it = icons_map.find("special");
|
|
|
|
if (special_icon_it != icons_map.end()) {
|
|
|
|
return special_icon_it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-10 18:00:01 +00:00
|
|
|
auto named_icon_it = icons_map.find(name());
|
2023-06-30 21:18:57 +00:00
|
|
|
if (named_icon_it != icons_map.end()) {
|
|
|
|
return named_icon_it->second;
|
|
|
|
}
|
|
|
|
|
2023-08-10 17:40:14 +00:00
|
|
|
if (is_visible()) {
|
|
|
|
auto visible_icon_it = icons_map.find("visible");
|
|
|
|
if (visible_icon_it != icons_map.end()) {
|
|
|
|
return visible_icon_it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-24 19:19:20 +00:00
|
|
|
if (is_empty()) {
|
|
|
|
auto empty_icon_it = icons_map.find("empty");
|
|
|
|
if (empty_icon_it != icons_map.end()) {
|
|
|
|
return empty_icon_it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-23 16:53:32 +00:00
|
|
|
if (is_persistent()) {
|
|
|
|
auto persistent_icon_it = icons_map.find("persistent");
|
|
|
|
if (persistent_icon_it != icons_map.end()) {
|
|
|
|
return persistent_icon_it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-30 21:18:57 +00:00
|
|
|
auto default_icon_it = icons_map.find("default");
|
|
|
|
if (default_icon_it != icons_map.end()) {
|
|
|
|
return default_icon_it->second;
|
|
|
|
}
|
2023-09-23 18:55:18 +00:00
|
|
|
|
|
|
|
return name_;
|
2023-06-30 21:18:57 +00:00
|
|
|
}
|
2023-07-15 17:43:22 +00:00
|
|
|
|
|
|
|
auto Workspace::handle_clicked(GdkEventButton *bt) -> bool {
|
2023-07-15 21:48:12 +00:00
|
|
|
try {
|
2023-07-23 16:53:32 +00:00
|
|
|
if (id() > 0) { // normal or numbered persistent
|
2023-07-15 21:48:12 +00:00
|
|
|
gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id()));
|
2023-07-23 16:53:32 +00:00
|
|
|
} else if (!is_special()) { // named
|
|
|
|
gIPC->getSocket1Reply("dispatch workspace name:" + name());
|
2023-07-15 21:48:12 +00:00
|
|
|
} else if (id() != -99) { // named special
|
2023-07-23 16:53:32 +00:00
|
|
|
gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name());
|
2023-07-15 21:48:12 +00:00
|
|
|
} else { // special
|
2023-07-16 18:22:14 +00:00
|
|
|
gIPC->getSocket1Reply("dispatch togglespecialworkspace");
|
2023-07-15 21:48:12 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
spdlog::error("Failed to dispatch workspace: {}", e.what());
|
2023-07-15 17:43:22 +00:00
|
|
|
}
|
2023-07-15 21:48:12 +00:00
|
|
|
return false;
|
2023-07-15 17:43:22 +00:00
|
|
|
}
|
|
|
|
|
2023-08-23 17:18:35 +00:00
|
|
|
void Workspaces::set_urgent_workspace(std::string windowaddress) {
|
|
|
|
const Json::Value clients_json = gIPC->getSocket1JsonReply("clients");
|
2023-09-04 23:50:57 +00:00
|
|
|
int workspace_id = -1;
|
2023-08-23 17:18:35 +00:00
|
|
|
|
|
|
|
for (Json::Value client_json : clients_json) {
|
|
|
|
if (client_json["address"].asString().ends_with(windowaddress)) {
|
|
|
|
workspace_id = client_json["workspace"]["id"].asInt();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto workspace =
|
|
|
|
std::find_if(workspaces_.begin(), workspaces_.end(),
|
|
|
|
[&](std::unique_ptr<Workspace> &x) { return x->id() == workspace_id; });
|
2023-09-04 23:50:57 +00:00
|
|
|
if (workspace != workspaces_.end()) {
|
2023-08-23 17:18:35 +00:00
|
|
|
workspace->get()->set_urgent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-09 16:53:00 +00:00
|
|
|
std::string Workspaces::get_rewrite(std::string window_class, std::string window_title) {
|
|
|
|
std::string window_repr_key = fmt::format("class<{}> title<{}>", window_class, window_title);
|
|
|
|
return window_rewrite_rules_.get(window_repr_key);
|
2023-09-22 21:18:02 +00:00
|
|
|
}
|
|
|
|
|
2023-06-27 23:52:01 +00:00
|
|
|
} // namespace waybar::modules::hyprland
|