Merge remote-tracking branch 'origin/master' into pr-5068
This commit is contained in:
@@ -54,21 +54,28 @@ void Language::onCmd(const struct Ipc::ipc_response& res) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto payload = parser_.parse(res.payload);
|
||||
std::vector<std::string> used_layouts;
|
||||
// Display current layout of a device with a maximum count of layouts, expecting that all will
|
||||
// be OK
|
||||
// Union layout names across every keyboard input so hot-plugged devices contribute their
|
||||
// layouts to the map. Track the device with the most layouts to seed the initially displayed
|
||||
// layout, matching the previous behaviour at startup.
|
||||
Json::ArrayIndex max_id = 0, max = 0;
|
||||
for (Json::ArrayIndex i = 0; i < payload.size(); i++) {
|
||||
auto size = payload[i][XKB_LAYOUT_NAMES_KEY].size();
|
||||
if (size > max) {
|
||||
max = size;
|
||||
if (payload[i]["type"].asString() != "keyboard") continue;
|
||||
const auto& names = payload[i][XKB_LAYOUT_NAMES_KEY];
|
||||
if (names.size() > max) {
|
||||
max = names.size();
|
||||
max_id = i;
|
||||
}
|
||||
for (const auto& layout : names) {
|
||||
const auto name = layout.asString();
|
||||
if (std::find(used_layouts.begin(), used_layouts.end(), name) == used_layouts.end()) {
|
||||
used_layouts.push_back(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& layout : payload[max_id][XKB_LAYOUT_NAMES_KEY]) {
|
||||
used_layouts.push_back(layout.asString());
|
||||
}
|
||||
|
||||
// Rebuild from scratch so init_layouts_map's duplicate-suffix pass doesn't compound across
|
||||
// refreshes (e.g. "us" -> "us1" -> "us11").
|
||||
layouts_map_.clear();
|
||||
init_layouts_map(used_layouts);
|
||||
set_current_layout(payload[max_id][XKB_ACTIVE_LAYOUT_NAME_KEY].asString());
|
||||
dp.emit();
|
||||
@@ -82,13 +89,26 @@ void Language::onEvent(const struct Ipc::ipc_response& res) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool refresh_inputs = false;
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto payload = parser_.parse(res.payload)["input"];
|
||||
if (payload["type"].asString() == "keyboard") {
|
||||
set_current_layout(payload[XKB_ACTIVE_LAYOUT_NAME_KEY].asString());
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto root = parser_.parse(res.payload);
|
||||
auto change = root["change"].asString();
|
||||
auto payload = root["input"];
|
||||
if (payload["type"].asString() == "keyboard") {
|
||||
// A device was added or its keymap changed - the layout set may have grown, so refresh
|
||||
// layouts_map_ via IPC_GET_INPUTS once we've released mutex_.
|
||||
refresh_inputs = (change == "added" || change == "xkb_keymap");
|
||||
set_current_layout(payload[XKB_ACTIVE_LAYOUT_NAME_KEY].asString());
|
||||
}
|
||||
dp.emit();
|
||||
}
|
||||
// sendCmd is synchronous: it blocks on the IPC reply and then emits signal_cmd on this same
|
||||
// thread, which lands in onCmd and re-locks mutex_. Must call it with mutex_ released.
|
||||
if (refresh_inputs) {
|
||||
ipc_.sendCmd(IPC_GET_INPUTS);
|
||||
}
|
||||
dp.emit();
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Language: {}", e.what());
|
||||
}
|
||||
@@ -125,8 +145,15 @@ auto Language::update() -> void {
|
||||
}
|
||||
|
||||
auto Language::set_current_layout(const std::string& current_layout) -> void {
|
||||
// Guard against unknown / empty layout names: transient virtual keyboards (e.g. wtype) and
|
||||
// hot-plugged devices whose layouts haven't made it into the map yet would otherwise blank out
|
||||
// layout_ via map::operator[]'s default-construct-on-miss.
|
||||
auto it = layouts_map_.find(current_layout);
|
||||
if (it == layouts_map_.end()) {
|
||||
return;
|
||||
}
|
||||
label_.get_style_context()->remove_class(layout_.short_name);
|
||||
layout_ = layouts_map_[current_layout];
|
||||
layout_ = it->second;
|
||||
label_.get_style_context()->add_class(layout_.short_name);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,12 +94,12 @@ auto Window::update() -> void {
|
||||
old_app_id_ = app_id_;
|
||||
}
|
||||
|
||||
label_.set_markup(waybar::util::rewriteString(
|
||||
setLabelMarkup(waybar::util::rewriteString(
|
||||
fmt::format(fmt::runtime(format_), fmt::arg("title", window_), fmt::arg("app_id", app_id_),
|
||||
fmt::arg("shell", shell_), fmt::arg("marks", marks_)),
|
||||
config_["rewrite"]));
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_markup(window_);
|
||||
setTooltipMarkup(window_);
|
||||
}
|
||||
|
||||
updateAppIcon();
|
||||
|
||||
@@ -69,6 +69,7 @@ Workspaces::Workspaces(const std::string& id, const Bar& bar, const Json::Value&
|
||||
m_windowRewriteRules = waybar::util::RegexCollection(
|
||||
windowRewrite, std::move(windowRewriteDefault), windowRewritePriorityFunction);
|
||||
}
|
||||
populateIgnoreWorkspacesConfig(config);
|
||||
ipc_.subscribe(R"(["workspace"])");
|
||||
ipc_.subscribe(R"(["window"])");
|
||||
ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent));
|
||||
@@ -97,6 +98,36 @@ void Workspaces::onEvent(const struct Ipc::ipc_response& res) {
|
||||
}
|
||||
}
|
||||
|
||||
auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value& config) -> void {
|
||||
auto ignoreWorkspaces = config["ignore-workspaces"];
|
||||
if (ignoreWorkspaces.isArray()) {
|
||||
for (const auto& workspaceRegex : ignoreWorkspaces) {
|
||||
if (workspaceRegex.isString()) {
|
||||
std::string ruleString = workspaceRegex.asString();
|
||||
try {
|
||||
const std::regex rule{ruleString, std::regex_constants::icase};
|
||||
m_ignoreWorkspaces.emplace_back(rule);
|
||||
} catch (const std::regex_error& e) {
|
||||
spdlog::error("Invalid rule {}: {}", ruleString, e.what());
|
||||
}
|
||||
} else {
|
||||
spdlog::error("Not a string: '{}'", workspaceRegex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Workspaces::isWorkspaceIgnored(std::string const& name) {
|
||||
for (auto& rule : m_ignoreWorkspaces) {
|
||||
if (std::regex_match(name, rule)) {
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Workspaces::onCmd(const struct Ipc::ipc_response& res) {
|
||||
if (res.type == IPC_GET_TREE) {
|
||||
try {
|
||||
@@ -118,8 +149,9 @@ void Workspaces::onCmd(const struct Ipc::ipc_response& res) {
|
||||
});
|
||||
|
||||
for (auto& output : outputs) {
|
||||
std::copy(output["nodes"].begin(), output["nodes"].end(),
|
||||
std::back_inserter(workspaces_));
|
||||
std::copy_if(
|
||||
output["nodes"].begin(), output["nodes"].end(), std::back_inserter(workspaces_),
|
||||
[&](const auto& node) { return !(isWorkspaceIgnored(node["name"].asString())); });
|
||||
std::copy(output["floating_nodes"].begin(), output["floating_nodes"].end(),
|
||||
std::back_inserter(workspaces_));
|
||||
}
|
||||
@@ -143,7 +175,8 @@ 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) {
|
||||
if (output.asString() == bar_.output->name) {
|
||||
auto output_name = output.asString();
|
||||
if (output_name == bar_.output->name || output_name == bar_.output->identifier) {
|
||||
Json::Value v;
|
||||
v["name"] = p_w_name;
|
||||
v["target_output"] = bar_.output->name;
|
||||
@@ -229,7 +262,10 @@ bool Workspaces::filterButtons() {
|
||||
auto ws = std::find_if(workspaces_.begin(), workspaces_.end(),
|
||||
[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 {
|
||||
@@ -321,6 +357,18 @@ auto Workspaces::update() -> void {
|
||||
button.get_style_context()->remove_class("empty");
|
||||
}
|
||||
if ((*it)["output"].isString()) {
|
||||
// Simply attempt to remove all output classes every time to reset output classes. This works
|
||||
// even if a class has not been previously added to the style context.
|
||||
for (const auto &oclass : config_["output-classes"]) {
|
||||
button.get_style_context()->remove_class(oclass.asString());
|
||||
}
|
||||
// If output-classes contains a class for output associated with current workspace button, add
|
||||
// the class to its style context.
|
||||
std::string output_name = (*it)["output"].asString();
|
||||
if (config_["output-classes"].isMember(output_name) &&
|
||||
config_["output-classes"][output_name].isString()) {
|
||||
button.get_style_context()->add_class(config_["output-classes"][output_name].asString());
|
||||
}
|
||||
if (((*it)["output"].asString()) == bar_.output->name) {
|
||||
button.get_style_context()->add_class("current_output");
|
||||
} else {
|
||||
@@ -329,24 +377,44 @@ auto Workspaces::update() -> void {
|
||||
} else {
|
||||
button.get_style_context()->remove_class("current_output");
|
||||
}
|
||||
std::string output = (*it)["name"].asString();
|
||||
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()) {
|
||||
updateWindows((*it), windows);
|
||||
}
|
||||
|
||||
auto index = (*it)["num"].asInt();
|
||||
|
||||
if (config_["format"].isString()) {
|
||||
auto format = config_["format"].asString();
|
||||
output = fmt::format(
|
||||
fmt::runtime(format), fmt::arg("icon", getIcon(output, *it)), fmt::arg("value", output),
|
||||
fmt::arg("name", trimWorkspaceName(output)), fmt::arg("index", (*it)["num"].asString()),
|
||||
fmt::arg("windows",
|
||||
windows.substr(0, windows.length() - m_formatWindowSeparator.length())),
|
||||
fmt::arg("output", (*it)["output"].asString()));
|
||||
std::string format;
|
||||
if (config_["format-for-negative-index"].isString() && index < 0) {
|
||||
format = config_["format-for-negative-index"].asString();
|
||||
} else {
|
||||
format = config_["format"].asString();
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
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(output);
|
||||
static_cast<Gtk::Label*>(button.get_children()[0])->set_markup(full_name);
|
||||
} else {
|
||||
button.set_label(output);
|
||||
button.set_label(full_name);
|
||||
}
|
||||
onButtonReady(*it, button);
|
||||
}
|
||||
@@ -369,11 +437,14 @@ 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) {
|
||||
spdlog::error("Workspaces: {}", e.what());
|
||||
|
||||
Reference in New Issue
Block a user