Merge branch 'master' into niri-column

This commit is contained in:
Alexis Rouillard
2026-07-03 22:22:02 +02:00
committed by GitHub
220 changed files with 5914 additions and 2736 deletions
+49 -56
View File
@@ -17,19 +17,21 @@
#include "giomm/dataoutputstream.h"
#include "giomm/unixinputstream.h"
#include "giomm/unixoutputstream.h"
#include "util/scoped_fd.hpp"
namespace waybar::modules::niri {
IPC::IPC() { startIPC(); }
int IPC::connectToSocket() {
const char *socket_path = getenv("NIRI_SOCKET");
const char* socket_path = getenv("NIRI_SOCKET");
if (socket_path == nullptr) {
spdlog::warn("Niri is not running, niri IPC will not be available.");
return -1;
throw std::runtime_error("Niri IPC: NIRI_SOCKET was not set! (Is Niri running?)");
}
struct sockaddr_un addr;
int socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
util::ScopedFd socketfd(socket(AF_UNIX, SOCK_STREAM, 0));
if (socketfd == -1) {
throw std::runtime_error("socketfd failed");
@@ -43,27 +45,19 @@ int IPC::connectToSocket() {
int l = sizeof(struct sockaddr_un);
if (connect(socketfd, (struct sockaddr *)&addr, l) == -1) {
close(socketfd);
if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) {
throw std::runtime_error("unable to connect");
}
return socketfd;
return socketfd.release();
}
void IPC::startIPC() {
// will start IPC and relay events to parseIPC
std::thread([&]() {
int socketfd;
try {
socketfd = connectToSocket();
} catch (std::exception &e) {
spdlog::error("Niri IPC: failed to start, reason: {}", e.what());
return;
}
if (socketfd == -1) return;
int socketfd = connectToSocket();
std::thread([this, socketfd]() {
spdlog::info("Niri IPC starting");
auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
@@ -87,7 +81,7 @@ void IPC::startIPC() {
try {
parseIPC(line);
} catch (std::exception &e) {
} catch (std::exception& e) {
spdlog::warn("Failed to parse IPC message: {}, reason: {}", line, e.what());
} catch (...) {
throw;
@@ -98,7 +92,7 @@ void IPC::startIPC() {
}).detach();
}
void IPC::parseIPC(const std::string &line) {
void IPC::parseIPC(const std::string& line) {
const auto ev = parser_.parse(line);
const auto members = ev.getMemberNames();
if (members.size() != 1) throw std::runtime_error("Event must have a single member");
@@ -106,28 +100,28 @@ void IPC::parseIPC(const std::string &line) {
{
auto lock = lockData();
if (const auto &payload = ev["WorkspacesChanged"]) {
if (const auto& payload = ev["WorkspacesChanged"]) {
workspaces_.clear();
const auto &values = payload["workspaces"];
const auto& values = payload["workspaces"];
std::copy(values.begin(), values.end(), std::back_inserter(workspaces_));
std::sort(workspaces_.begin(), workspaces_.end(), [](const auto &a, const auto &b) {
const auto &aOutput = a["output"].asString();
const auto &bOutput = b["output"].asString();
std::sort(workspaces_.begin(), workspaces_.end(), [](const auto& a, const auto& b) {
const auto& aOutput = a["output"].asString();
const auto& bOutput = b["output"].asString();
const auto aIdx = a["idx"].asUInt();
const auto bIdx = b["idx"].asUInt();
if (aOutput == bOutput) return aIdx < bIdx;
return aOutput < bOutput;
});
} else if (const auto &payload = ev["WorkspaceActivated"]) {
} else if (const auto& payload = ev["WorkspaceActivated"]) {
const auto id = payload["id"].asUInt64();
const auto focused = payload["focused"].asBool();
auto it = std::find_if(workspaces_.begin(), workspaces_.end(),
[id](const auto &ws) { return ws["id"].asUInt64() == id; });
[id](const auto& ws) { return ws["id"].asUInt64() == id; });
if (it != workspaces_.end()) {
const auto &ws = *it;
const auto &output = ws["output"].asString();
for (auto &ws : workspaces_) {
const auto& ws = *it;
const auto& output = ws["output"].asString();
for (auto& ws : workspaces_) {
const auto got_activated = (ws["id"].asUInt64() == id);
if (ws["output"] == output) ws["is_active"] = got_activated;
@@ -136,70 +130,70 @@ void IPC::parseIPC(const std::string &line) {
} else {
spdlog::error("Activated unknown workspace");
}
} else if (const auto &payload = ev["WorkspaceActiveWindowChanged"]) {
} else if (const auto& payload = ev["WorkspaceActiveWindowChanged"]) {
const auto workspaceId = payload["workspace_id"].asUInt64();
auto it = std::find_if(workspaces_.begin(), workspaces_.end(), [workspaceId](const auto &ws) {
auto it = std::find_if(workspaces_.begin(), workspaces_.end(), [workspaceId](const auto& ws) {
return ws["id"].asUInt64() == workspaceId;
});
if (it != workspaces_.end()) {
auto &ws = *it;
auto& ws = *it;
ws["active_window_id"] = payload["active_window_id"];
} else {
spdlog::error("Active window changed on unknown workspace");
}
} else if (const auto &payload = ev["WorkspaceUrgencyChanged"]) {
} else if (const auto& payload = ev["WorkspaceUrgencyChanged"]) {
const auto id = payload["id"].asUInt64();
const auto urgent = payload["urgent"].asBool();
auto it = std::find_if(workspaces_.begin(), workspaces_.end(),
[id](const auto &ws) { return ws["id"].asUInt64() == id; });
[id](const auto& ws) { return ws["id"].asUInt64() == id; });
if (it != workspaces_.end()) {
auto &ws = *it;
auto& ws = *it;
ws["is_urgent"] = urgent;
} else {
spdlog::error("Urgency changed for unknown workspace");
}
} else if (const auto &payload = ev["KeyboardLayoutsChanged"]) {
const auto &layouts = payload["keyboard_layouts"];
const auto &names = layouts["names"];
} else if (const auto& payload = ev["KeyboardLayoutsChanged"]) {
const auto& layouts = payload["keyboard_layouts"];
const auto& names = layouts["names"];
keyboardLayoutCurrent_ = layouts["current_idx"].asUInt();
keyboardLayoutNames_.clear();
for (const auto &fullName : names) keyboardLayoutNames_.push_back(fullName.asString());
} else if (const auto &payload = ev["KeyboardLayoutSwitched"]) {
for (const auto& fullName : names) keyboardLayoutNames_.push_back(fullName.asString());
} else if (const auto& payload = ev["KeyboardLayoutSwitched"]) {
keyboardLayoutCurrent_ = payload["idx"].asUInt();
} else if (const auto &payload = ev["WindowsChanged"]) {
} else if (const auto& payload = ev["WindowsChanged"]) {
windows_.clear();
const auto &values = payload["windows"];
const auto& values = payload["windows"];
std::copy(values.begin(), values.end(), std::back_inserter(windows_));
} else if (const auto &payload = ev["WindowOpenedOrChanged"]) {
const auto &window = payload["window"];
} else if (const auto& payload = ev["WindowOpenedOrChanged"]) {
const auto& window = payload["window"];
const auto id = window["id"].asUInt64();
auto it = std::find_if(windows_.begin(), windows_.end(),
[id](const auto &win) { return win["id"].asUInt64() == id; });
[id](const auto& win) { return win["id"].asUInt64() == id; });
if (it == windows_.end()) {
windows_.push_back(window);
if (window["is_focused"].asBool()) {
for (auto &win : windows_) {
for (auto& win : windows_) {
win["is_focused"] = win["id"].asUInt64() == id;
}
}
} else {
*it = window;
}
} else if (const auto &payload = ev["WindowClosed"]) {
} else if (const auto& payload = ev["WindowClosed"]) {
const auto id = payload["id"].asUInt64();
auto it = std::find_if(windows_.begin(), windows_.end(),
[id](const auto &win) { return win["id"].asUInt64() == id; });
[id](const auto& win) { return win["id"].asUInt64() == id; });
if (it != windows_.end()) {
windows_.erase(it);
} else {
spdlog::error("Unknown window closed");
}
} else if (const auto &payload = ev["WindowFocusChanged"]) {
} else if (const auto& payload = ev["WindowFocusChanged"]) {
const auto focused = !payload["id"].isNull();
const auto id = payload["id"].asUInt64();
for (auto &win : windows_) {
for (auto& win : windows_) {
win["is_focused"] = focused && win["id"].asUInt64() == id;
}
} else if (const auto &payload = ev["WindowLayoutsChanged"]) {
@@ -219,14 +213,14 @@ void IPC::parseIPC(const std::string &line) {
std::unique_lock lock(callbackMutex_);
for (auto &[eventname, handler] : callbacks_) {
for (auto& [eventname, handler] : callbacks_) {
if (eventname == members[0]) {
handler->onEvent(ev);
}
}
}
void IPC::registerForIPC(const std::string &ev, EventHandler *ev_handler) {
void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) {
if (ev_handler == nullptr) {
return;
}
@@ -235,7 +229,7 @@ void IPC::registerForIPC(const std::string &ev, EventHandler *ev_handler) {
callbacks_.emplace_back(ev, ev_handler);
}
void IPC::unregisterForIPC(EventHandler *ev_handler) {
void IPC::unregisterForIPC(EventHandler* ev_handler) {
if (ev_handler == nullptr) {
return;
}
@@ -243,7 +237,7 @@ void IPC::unregisterForIPC(EventHandler *ev_handler) {
std::unique_lock lock(callbackMutex_);
for (auto it = callbacks_.begin(); it != callbacks_.end();) {
auto &[eventname, handler] = *it;
auto& [eventname, handler] = *it;
if (handler == ev_handler) {
it = callbacks_.erase(it);
} else {
@@ -252,9 +246,8 @@ void IPC::unregisterForIPC(EventHandler *ev_handler) {
}
}
Json::Value IPC::send(const Json::Value &request) {
int socketfd = connectToSocket();
if (socketfd == -1) throw std::runtime_error("Niri is not running");
Json::Value IPC::send(const Json::Value& request) {
util::ScopedFd socketfd(connectToSocket());
auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false);
+11 -10
View File
@@ -8,7 +8,7 @@
namespace waybar::modules::niri {
Language::Language(const std::string &id, const Bar &bar, const Json::Value &config)
Language::Language(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "language", id, "{}", 0, false), bar_(bar) {
label_.hide();
@@ -32,7 +32,8 @@ void Language::updateFromIPC() {
auto ipcLock = gIPC->lockData();
layouts_.clear();
for (const auto &fullName : gIPC->keyboardLayoutNames()) layouts_.push_back(getLayout(fullName));
layouts_.reserve(gIPC->keyboardLayoutNames().size());
for (const auto& fullName : gIPC->keyboardLayoutNames()) layouts_.push_back(getLayout(fullName));
current_idx_ = gIPC->keyboardLayoutCurrent();
}
@@ -51,7 +52,7 @@ void Language::doUpdate() {
label_.hide();
return;
}
const auto &layout = layouts_[current_idx_];
const auto& layout = layouts_[current_idx_];
spdlog::debug("niri language update with full name {}", layout.full_name);
spdlog::debug("niri language update with short name {}", layout.short_name);
@@ -84,7 +85,7 @@ void Language::doUpdate() {
spdlog::debug("niri language formatted layout name {}", layoutName);
if (!format_.empty()) {
if (!layoutName.empty()) {
label_.show();
label_.set_markup(layoutName);
} else {
@@ -97,7 +98,7 @@ void Language::update() {
ALabel::update();
}
void Language::onEvent(const Json::Value &ev) {
void Language::onEvent(const Json::Value& ev) {
if (ev["KeyboardLayoutsChanged"]) {
updateFromIPC();
} else if (ev["KeyboardLayoutSwitched"]) {
@@ -109,11 +110,11 @@ void Language::onEvent(const Json::Value &ev) {
dp.emit();
}
Language::Layout Language::getLayout(const std::string &fullName) {
auto *const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES);
Language::Layout Language::getLayout(const std::string& fullName) {
auto* const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES);
rxkb_context_parse_default_ruleset(context);
rxkb_layout *layout = rxkb_layout_first(context);
rxkb_layout* layout = rxkb_layout_first(context);
while (layout != nullptr) {
std::string nameOfLayout = rxkb_layout_get_description(layout);
@@ -123,10 +124,10 @@ Language::Layout Language::getLayout(const std::string &fullName) {
}
auto name = std::string(rxkb_layout_get_name(layout));
const auto *variantPtr = rxkb_layout_get_variant(layout);
const auto* variantPtr = rxkb_layout_get_variant(layout);
std::string variant = variantPtr == nullptr ? "" : std::string(variantPtr);
const auto *descriptionPtr = rxkb_layout_get_brief(layout);
const auto* descriptionPtr = rxkb_layout_get_brief(layout);
std::string description = descriptionPtr == nullptr ? "" : std::string(descriptionPtr);
Layout info = Layout{nameOfLayout, name, variant, description};
+10 -10
View File
@@ -9,7 +9,7 @@
namespace waybar::modules::niri {
Window::Window(const std::string &id, const Bar &bar, const Json::Value &config)
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) {
if (!gIPC) gIPC = std::make_unique<IPC>();
@@ -24,16 +24,16 @@ Window::Window(const std::string &id, const Bar &bar, const Json::Value &config)
Window::~Window() { gIPC->unregisterForIPC(this); }
void Window::onEvent(const Json::Value &ev) { dp.emit(); }
void Window::onEvent(const Json::Value& ev) { dp.emit(); }
void Window::doUpdate() {
auto ipcLock = gIPC->lockData();
const auto &windows = gIPC->windows();
const auto &workspaces = gIPC->workspaces();
const auto& windows = gIPC->windows();
const auto& workspaces = gIPC->workspaces();
const auto separateOutputs = config_["separate-outputs"].asBool();
const auto ws_it = std::find_if(workspaces.cbegin(), workspaces.cend(), [&](const auto &ws) {
const auto ws_it = std::find_if(workspaces.cbegin(), workspaces.cend(), [&](const auto& ws) {
if (separateOutputs) {
return ws["is_active"].asBool() && ws["output"].asString() == bar_.output->name;
}
@@ -47,13 +47,13 @@ void Window::doUpdate() {
} else {
const auto id = (*ws_it)["active_window_id"].asUInt64();
it = std::find_if(windows.cbegin(), windows.cend(),
[id](const auto &win) { return win["id"].asUInt64() == id; });
[id](const auto& win) { return win["id"].asUInt64() == id; });
}
setClass("empty", ws_it == workspaces.cend() || (*ws_it)["active_window_id"].isNull());
if (it != windows.cend()) {
const auto &window = *it;
const auto& window = *it;
auto max_col = -1;
for (const auto &win : windows) {
@@ -78,11 +78,11 @@ void Window::doUpdate() {
updateAppIconName(appId, "");
if (tooltipEnabled()) label_.set_tooltip_text(title);
if (tooltipEnabled()) label_.set_tooltip_markup(title);
const auto id = window["id"].asUInt64();
const auto workspaceId = window["workspace_id"].asUInt64();
const auto isSolo = std::none_of(windows.cbegin(), windows.cend(), [&](const auto &win) {
const auto isSolo = std::none_of(windows.cbegin(), windows.cend(), [&](const auto& win) {
return win["id"].asUInt64() != id && win["workspace_id"].asUInt64() == workspaceId;
});
setClass("solo", isSolo);
@@ -106,7 +106,7 @@ void Window::update() {
AAppIconLabel::update();
}
void Window::setClass(const std::string &className, bool enable) {
void Window::setClass(const std::string& className, bool enable) {
auto styleContext = bar_.window.get_style_context();
if (enable) {
if (!styleContext->has_class(className)) {
+29 -22
View File
@@ -6,7 +6,7 @@
namespace waybar::modules::niri {
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, false), bar_(bar), box_(bar.orientation, 0) {
box_.set_name("workspaces");
if (!id.empty()) {
@@ -27,16 +27,16 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
Workspaces::~Workspaces() { gIPC->unregisterForIPC(this); }
void Workspaces::onEvent(const Json::Value &ev) { dp.emit(); }
void Workspaces::onEvent(const Json::Value& ev) { dp.emit(); }
void Workspaces::doUpdate() {
auto ipcLock = gIPC->lockData();
const auto alloutputs = config_["all-outputs"].asBool();
std::vector<Json::Value> my_workspaces;
const auto &workspaces = gIPC->workspaces();
const auto& workspaces = gIPC->workspaces();
std::copy_if(workspaces.cbegin(), workspaces.cend(), std::back_inserter(my_workspaces),
[&](const auto &ws) {
[&](const auto& ws) {
if (alloutputs) return true;
return ws["output"].asString() == bar_.output->name;
});
@@ -44,7 +44,7 @@ void Workspaces::doUpdate() {
// Remove buttons for removed workspaces.
for (auto it = buttons_.begin(); it != buttons_.end();) {
auto ws = std::find_if(my_workspaces.begin(), my_workspaces.end(),
[it](const auto &ws) { return ws["id"].asUInt64() == it->first; });
[it](const auto& ws) { return ws["id"].asUInt64() == it->first; });
if (ws == my_workspaces.end()) {
it = buttons_.erase(it);
} else {
@@ -53,9 +53,9 @@ void Workspaces::doUpdate() {
}
// Add buttons for new workspaces, update existing ones.
for (const auto &ws : my_workspaces) {
for (const auto& ws : my_workspaces) {
auto bit = buttons_.find(ws["id"].asUInt64());
auto &button = bit == buttons_.end() ? addButton(ws) : bit->second;
auto& button = bit == buttons_.end() ? addButton(ws) : bit->second;
auto style_context = button.get_style_context();
if (ws["is_focused"].asBool())
@@ -103,17 +103,24 @@ void Workspaces::doUpdate() {
fmt::arg("output", ws["output"].asString()));
}
if (!config_["disable-markup"].asBool()) {
static_cast<Gtk::Label *>(button.get_children()[0])->set_markup(name);
auto* child = gtk_bin_get_child(GTK_BIN(button.gobj()));
if (child != nullptr && GTK_IS_LABEL(child))
gtk_label_set_markup(GTK_LABEL(child), name.c_str());
} else {
button.set_label(name);
}
if (config_["current-only"].asBool()) {
const auto *property = alloutputs ? "is_focused" : "is_active";
const auto* property = alloutputs ? "is_focused" : "is_active";
if (ws[property].asBool())
button.show();
else
button.hide();
} else if (config_["hide-empty"].asBool()) {
if (ws["active_window_id"].isNull() && !ws["is_focused"].asBool())
button.hide();
else
button.show();
} else {
button.show();
}
@@ -121,12 +128,12 @@ void Workspaces::doUpdate() {
// Refresh the button order.
for (auto it = my_workspaces.cbegin(); it != my_workspaces.cend(); ++it) {
const auto &ws = *it;
const auto& ws = *it;
auto pos = ws["idx"].asUInt() - 1;
if (alloutputs) pos = it - my_workspaces.cbegin();
auto &button = buttons_[ws["id"].asUInt64()];
auto& button = buttons_[ws["id"].asUInt64()];
box_.reorder_child(button, pos);
}
}
@@ -136,7 +143,7 @@ void Workspaces::update() {
AModule::update();
}
Gtk::Button &Workspaces::addButton(const Json::Value &ws) {
Gtk::Button& Workspaces::addButton(const Json::Value& ws) {
std::string name;
if (ws["name"]) {
name = ws["name"].asString();
@@ -145,7 +152,7 @@ Gtk::Button &Workspaces::addButton(const Json::Value &ws) {
}
auto pair = buttons_.emplace(ws["id"].asUInt64(), name);
auto &&button = pair.first->second;
auto&& button = pair.first->second;
box_.pack_start(button, false, false, 0);
button.set_relief(Gtk::RELIEF_NONE);
if (!config_["disable-click"].asBool()) {
@@ -154,13 +161,13 @@ Gtk::Button &Workspaces::addButton(const Json::Value &ws) {
try {
// {"Action":{"FocusWorkspace":{"reference":{"Id":1}}}}
Json::Value request(Json::objectValue);
auto &action = (request["Action"] = Json::Value(Json::objectValue));
auto &focusWorkspace = (action["FocusWorkspace"] = Json::Value(Json::objectValue));
auto &reference = (focusWorkspace["reference"] = Json::Value(Json::objectValue));
auto& action = (request["Action"] = Json::Value(Json::objectValue));
auto& focusWorkspace = (action["FocusWorkspace"] = Json::Value(Json::objectValue));
auto& reference = (focusWorkspace["reference"] = Json::Value(Json::objectValue));
reference["Id"] = id;
IPC::send(request);
} catch (const std::exception &e) {
} catch (const std::exception& e) {
spdlog::error("Error switching workspace: {}", e.what());
}
});
@@ -168,20 +175,20 @@ Gtk::Button &Workspaces::addButton(const Json::Value &ws) {
return button;
}
std::string Workspaces::getIcon(const std::string &value, const Json::Value &ws) {
const auto &icons = config_["format-icons"];
std::string Workspaces::getIcon(const std::string& value, const Json::Value& ws) {
const auto& icons = config_["format-icons"];
if (!icons) return value;
if (ws["is_urgent"].asBool() && icons["urgent"]) return icons["urgent"].asString();
if (ws["active_window_id"].isNull() && icons["empty"]) return icons["empty"].asString();
if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString();
if (ws["is_focused"].asBool() && icons["focused"]) return icons["focused"].asString();
if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString();
if (ws["active_window_id"].isNull() && icons["empty"]) return icons["empty"].asString();
if (ws["name"]) {
const auto &name = ws["name"].asString();
const auto& name = ws["name"].asString();
if (icons[name]) return icons[name].asString();
}