Merge remote-tracking branch 'origin/master' into feat/sway-workspaces-format-for-negative-indices

# Conflicts:
#	src/modules/sway/workspaces.cpp
This commit is contained in:
Alex
2026-07-03 22:11:45 +02:00
206 changed files with 4692 additions and 2537 deletions
+73 -66
View File
@@ -10,10 +10,10 @@ namespace waybar::modules::sway {
// Helper function to assign a number to a workspace, just like sway. In fact
// this is taken quite verbatim from `sway/ipc-json.c`.
int Workspaces::convertWorkspaceNameToNum(std::string name) {
int Workspaces::convertWorkspaceNameToNum(const std::string& name) {
if (isdigit(name[0]) != 0) {
errno = 0;
char *endptr = nullptr;
char* endptr = nullptr;
long long parsed_num = strtoll(name.c_str(), &endptr, 10);
if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == name.c_str()) {
return -1;
@@ -23,7 +23,7 @@ int Workspaces::convertWorkspaceNameToNum(std::string name) {
return -1;
}
int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) {
int Workspaces::windowRewritePriorityFunction(std::string const& window_rule) {
// Rules that match against title are prioritized
// Rules that don't specify if they're matching against either title or class are deprioritized
bool const hasTitle = window_rule.find("title") != std::string::npos;
@@ -41,12 +41,12 @@ int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) {
return 0;
}
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
Workspaces::Workspaces(const std::string& id, const Bar& bar, const Json::Value& config)
: AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()),
bar_(bar),
box_(bar.orientation, 0) {
if (config["format-icons"]["high-priority-named"].isArray()) {
for (const auto &it : config["format-icons"]["high-priority-named"]) {
for (const auto& it : config["format-icons"]["high-priority-named"]) {
high_priority_named_.push_back(it.asString());
}
}
@@ -61,9 +61,9 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
} else {
m_formatWindowSeparator = " ";
}
const Json::Value &windowRewrite = config["window-rewrite"];
const Json::Value& windowRewrite = config["window-rewrite"];
if (windowRewrite.isObject()) {
const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"];
const Json::Value& windowRewriteDefaultConfig = config["window-rewrite-default"];
std::string windowRewriteDefault =
windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?";
m_windowRewriteRules = waybar::util::RegexCollection(
@@ -75,7 +75,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd));
ipc_.sendCmd(IPC_GET_TREE);
if (config["enable-bar-scroll"].asBool()) {
auto &window = const_cast<Bar &>(bar_).window;
auto& window = const_cast<Bar&>(bar_).window;
window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
}
@@ -83,21 +83,21 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
ipc_.setWorker([this] {
try {
ipc_.handleEvent();
} catch (const std::exception &e) {
} catch (const std::exception& e) {
spdlog::error("Workspaces: {}", e.what());
}
});
}
void Workspaces::onEvent(const struct Ipc::ipc_response &res) {
void Workspaces::onEvent(const struct Ipc::ipc_response& res) {
try {
ipc_.sendCmd(IPC_GET_TREE);
} catch (const std::exception &e) {
} catch (const std::exception& e) {
spdlog::error("Workspaces: {}", e.what());
}
}
void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
void Workspaces::onCmd(const struct Ipc::ipc_response& res) {
if (res.type == IPC_GET_TREE) {
try {
{
@@ -107,7 +107,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
std::vector<Json::Value> outputs;
bool alloutputs = config_["all-outputs"].asBool();
std::copy_if(payload["nodes"].begin(), payload["nodes"].end(), std::back_inserter(outputs),
[&](const auto &output) {
[&](const auto& output) {
if (alloutputs && output["name"].asString() != "__i3") {
return true;
}
@@ -117,7 +117,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
return false;
});
for (auto &output : outputs) {
for (auto& output : outputs) {
std::copy(output["nodes"].begin(), output["nodes"].end(),
std::back_inserter(workspaces_));
std::copy(output["floating_nodes"].begin(), output["floating_nodes"].end(),
@@ -126,13 +126,13 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
// adding persistent workspaces (as per the config file)
if (config_["persistent-workspaces"].isObject()) {
const Json::Value &p_workspaces = config_["persistent-workspaces"];
const Json::Value& p_workspaces = config_["persistent-workspaces"];
const std::vector<std::string> p_workspaces_names = p_workspaces.getMemberNames();
for (const std::string &p_w_name : p_workspaces_names) {
const Json::Value &p_w = p_workspaces[p_w_name];
for (const std::string& p_w_name : p_workspaces_names) {
const Json::Value& p_w = p_workspaces[p_w_name];
auto it = std::find_if(workspaces_.begin(), workspaces_.end(),
[&p_w_name](const Json::Value &node) {
[&p_w_name](const Json::Value& node) {
return node["name"].asString() == p_w_name;
});
@@ -142,7 +142,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
if (p_w.isArray() && !p_w.empty()) {
// Adding to target outputs
for (const Json::Value &output : p_w) {
for (const Json::Value& output : p_w) {
if (output.asString() == bar_.output->name) {
Json::Value v;
v["name"] = p_w_name;
@@ -185,10 +185,10 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
// agreement that the "workspace prev/next" commands may not follow
// the order displayed in Waybar.
int max_num = -1;
for (auto &workspace : workspaces_) {
for (auto& workspace : workspaces_) {
max_num = std::max(workspace["num"].asInt(), max_num);
}
for (auto &workspace : workspaces_) {
for (auto& workspace : workspaces_) {
auto workspace_num = workspace["num"].asInt();
if (workspace_num > -1) {
workspace["sort"] = workspace_num;
@@ -197,7 +197,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
}
}
std::sort(workspaces_.begin(), workspaces_.end(),
[this](const Json::Value &lhs, const Json::Value &rhs) {
[this](const Json::Value& lhs, const Json::Value& rhs) {
auto lname = lhs["name"].asString();
auto rname = rhs["name"].asString();
int l = lhs["sort"].asInt();
@@ -217,7 +217,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
});
}
dp.emit();
} catch (const std::exception &e) {
} catch (const std::exception& e) {
spdlog::error("Workspaces: {}", e.what());
}
}
@@ -227,9 +227,12 @@ bool Workspaces::filterButtons() {
bool needReorder = false;
for (auto it = buttons_.begin(); it != buttons_.end();) {
auto ws = std::find_if(workspaces_.begin(), workspaces_.end(),
[it](const auto &node) { return node["name"].asString() == it->first; });
[it](const auto& node) { return node["name"].asString() == it->first; });
if (ws == workspaces_.end() ||
(!config_["all-outputs"].asBool() && (*ws)["output"].asString() != bar_.output->name)) {
((*ws).isMember("target_output") ? (*ws)["target_output"].asString() != bar_.output->name &&
(*ws)["target_output"].asString() != ""
: !config_["all-outputs"].asBool() &&
(*ws)["output"].asString() != bar_.output->name)) {
it = buttons_.erase(it);
needReorder = true;
} else {
@@ -239,23 +242,23 @@ bool Workspaces::filterButtons() {
return needReorder;
}
bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) {
bool Workspaces::hasFlag(const Json::Value& node, const std::string& flag) {
if (node[flag].asBool()) {
return true;
}
if (std::any_of(node["nodes"].begin(), node["nodes"].end(),
[&](auto const &e) { return hasFlag(e, flag); })) {
[&](auto const& e) { return hasFlag(e, flag); })) {
return true;
}
if (std::any_of(node["floating_nodes"].begin(), node["floating_nodes"].end(),
[&](auto const &e) { return hasFlag(e, flag); })) {
[&](auto const& e) { return hasFlag(e, flag); })) {
return true;
}
return false;
}
void Workspaces::updateWindows(const Json::Value &node, std::string &windows) {
void Workspaces::updateWindows(const Json::Value& node, std::string& windows) {
if ((node["type"].asString() == "con" || node["type"].asString() == "floating_con") &&
node["name"].isString()) {
std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1);
@@ -274,10 +277,10 @@ void Workspaces::updateWindows(const Json::Value &node, std::string &windows) {
windows.append(m_formatWindowSeparator);
}
}
for (const Json::Value &child : node["nodes"]) {
for (const Json::Value& child : node["nodes"]) {
updateWindows(child, windows);
}
for (const Json::Value &child : node["floating_nodes"]) {
for (const Json::Value& child : node["floating_nodes"]) {
updateWindows(child, windows);
}
}
@@ -290,7 +293,7 @@ auto Workspaces::update() -> void {
if (bit == buttons_.end()) {
needReorder = true;
}
auto &button = bit == buttons_.end() ? addButton(*it) : bit->second;
auto& button = bit == buttons_.end() ? addButton(*it) : bit->second;
if (needReorder) {
box_.reorder_child(button, it - workspaces_.begin());
}
@@ -329,6 +332,12 @@ auto Workspaces::update() -> void {
} else {
button.get_style_context()->remove_class("current_output");
}
std::string full_name;
if (!config_["disable-markup"].asBool()) {
full_name = g_markup_escape_text((*it)["name"].asString().c_str(), -1);
} else {
full_name = (*it)["name"].asString();
}
std::string windows = "";
if (config_["window-rewrite"].isObject()) {
@@ -336,11 +345,10 @@ auto Workspaces::update() -> void {
}
auto index = (*it)["num"].asInt();
auto full_name = (*it)["name"].asString();
if (config_["format"].isString()) {
std::string format;
if(config_["format-for-negative-index"].isString() && index < 0) {
if (config_["format-for-negative-index"].isString() && index < 0) {
format = config_["format-for-negative-index"].asString();
} else {
format = config_["format"].asString();
@@ -349,21 +357,17 @@ auto Workspaces::update() -> void {
auto name = trimWorkspaceName(full_name);
auto output = (*it)["output"].asString();
auto icon = getIcon(full_name, *it);
auto separated_windows = windows.substr(0, windows.length() - m_formatWindowSeparator.length());
auto separated_windows =
windows.substr(0, windows.length() - m_formatWindowSeparator.length());
full_name = fmt::format(
fmt::runtime(format),
fmt::arg("index", index),
fmt::arg("name", name),
fmt::arg("value", full_name),
fmt::arg("output", output),
fmt::arg("icon", icon),
fmt::arg("windows", separated_windows)
);
full_name =
fmt::format(fmt::runtime(format), fmt::arg("index", index), fmt::arg("name", name),
fmt::arg("value", full_name), fmt::arg("output", output),
fmt::arg("icon", icon), fmt::arg("windows", separated_windows));
}
if (!config_["disable-markup"].asBool()) {
static_cast<Gtk::Label *>(button.get_children()[0])->set_markup(full_name);
static_cast<Gtk::Label*>(button.get_children()[0])->set_markup(full_name);
} else {
button.set_label(full_name);
}
@@ -373,9 +377,9 @@ auto Workspaces::update() -> void {
AModule::update();
}
Gtk::Button &Workspaces::addButton(const Json::Value &node) {
Gtk::Button& Workspaces::addButton(const Json::Value& node) {
auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString());
auto &&button = pair.first->second;
auto&& button = pair.first->second;
box_.pack_start(button, false, false, 0);
button.set_name("sway-workspace-" + node["name"].asString());
button.set_relief(Gtk::RELIEF_NONE);
@@ -388,13 +392,16 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) {
node["name"].asString(), node["target_output"].asString(),
"--no-auto-back-and-forth", node["name"].asString()));
} else {
ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace {} \"{}\"",
config_["disable-auto-back-and-forth"].asBool()
? "--no-auto-back-and-forth"
: "",
node["name"].asString()));
std::string flag = config_["disable-auto-back-and-forth"].asBool()
? "--no-auto-back-and-forth"
: "";
if (node["num"].asInt() >= 0) {
ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_number_cmd_, flag, node["num"].asInt()));
} else {
ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, flag, node["name"].asString()));
}
}
} catch (const std::exception &e) {
} catch (const std::exception& e) {
spdlog::error("Workspaces: {}", e.what());
}
});
@@ -402,18 +409,18 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) {
return button;
}
std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) {
std::string Workspaces::getIcon(const std::string& name, const Json::Value& node) {
std::vector<std::string> keys = {"high-priority-named", "urgent", "focused", name, "default"};
for (auto const &key : keys) {
for (auto const& key : keys) {
if (key == "high-priority-named") {
auto it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(),
[&](const std::string &member) { return member == name; });
[&](const std::string& member) { return member == name; });
if (it != high_priority_named_.end()) {
return config_["format-icons"][name].asString();
}
it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(),
[&](const std::string &member) {
[&](const std::string& member) {
return trimWorkspaceName(member) == trimWorkspaceName(name);
});
if (it != high_priority_named_.end()) {
@@ -436,8 +443,8 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node
return name;
}
bool Workspaces::handleScroll(GdkEventScroll *e) {
if (gdk_event_get_pointer_emulated((GdkEvent *)e) != 0) {
bool Workspaces::handleScroll(GdkEventScroll* e) {
if (gdk_event_get_pointer_emulated((GdkEvent*)e) != 0) {
/**
* Ignore emulated scroll events on window
*/
@@ -452,7 +459,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
bool alloutputs = config_["all-outputs"].asBool();
std::lock_guard<std::mutex> lock(mutex_);
auto it =
std::find_if(workspaces_.begin(), workspaces_.end(), [alloutputs](const auto &workspace) {
std::find_if(workspaces_.begin(), workspaces_.end(), [alloutputs](const auto& workspace) {
if (alloutputs) {
return hasFlag(workspace, "focused");
}
@@ -479,7 +486,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
}
try {
ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, "--no-auto-back-and-forth", name));
} catch (const std::exception &e) {
} catch (const std::exception& e) {
spdlog::error("Workspaces: {}", e.what());
}
if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) {
@@ -506,7 +513,7 @@ std::string Workspaces::getCycleWorkspace(std::vector<Json::Value>::iterator it,
return (*it)["name"].asString();
}
std::string Workspaces::trimWorkspaceName(std::string name) {
std::string Workspaces::trimWorkspaceName(const std::string& name) {
std::size_t found = name.find(':');
if (found != std::string::npos) {
return name.substr(found + 1);
@@ -514,7 +521,7 @@ std::string Workspaces::trimWorkspaceName(std::string name) {
return name;
}
bool is_focused_recursive(const Json::Value &node) {
bool is_focused_recursive(const Json::Value& node) {
// If a workspace has a focused container then get_tree will say
// that the workspace itself isn't focused. Therefore we need to
// check if any of its nodes are focused as well.
@@ -524,13 +531,13 @@ bool is_focused_recursive(const Json::Value &node) {
return true;
}
for (const auto &child : node["nodes"]) {
for (const auto& child : node["nodes"]) {
if (is_focused_recursive(child)) {
return true;
}
}
for (const auto &child : node["floating_nodes"]) {
for (const auto& child : node["floating_nodes"]) {
if (is_focused_recursive(child)) {
return true;
}
@@ -539,7 +546,7 @@ bool is_focused_recursive(const Json::Value &node) {
return false;
}
void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) {
void Workspaces::onButtonReady(const Json::Value& node, Gtk::Button& button) {
if (config_["current-only"].asBool()) {
if (is_focused_recursive(node)) {
button.show();