Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Pol Rivero
2025-03-08 17:18:31 +01:00
17 changed files with 216 additions and 171 deletions

View File

@ -17,4 +17,4 @@ jobs:
source: "." source: "."
extensions: "hpp,h,cpp,c" extensions: "hpp,h,cpp,c"
style: "file:.clang-format" style: "file:.clang-format"
clangFormatVersion: 18 clangFormatVersion: 19

View File

@ -9,7 +9,7 @@
- River (Mapping mode, Tags, Focused window name) - River (Mapping mode, Tags, Focused window name)
- Hyprland (Window Icons, Workspaces, Focused window name) - Hyprland (Window Icons, Workspaces, Focused window name)
- Niri (Workspaces, Focused window name, Language) - Niri (Workspaces, Focused window name, Language)
- DWL (Tags, Focused window name) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc) - DWL (Tags, Focused window name) [requires dwl ipc patch](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/ipc)
- Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Tray [#21](https://github.com/Alexays/Waybar/issues/21)
- Local time - Local time
- Battery - Battery

View File

@ -2,9 +2,9 @@
#include <filesystem> #include <filesystem>
#include <list> #include <list>
#include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread>
#include <utility> #include <utility>
#include "util/json.hpp" #include "util/json.hpp"
@ -19,7 +19,9 @@ class EventHandler {
class IPC { class IPC {
public: public:
IPC() { startIPC(); } IPC();
~IPC();
static IPC& inst();
void registerForIPC(const std::string& ev, EventHandler* ev_handler); void registerForIPC(const std::string& ev, EventHandler* ev_handler);
void unregisterForIPC(EventHandler* handler); void unregisterForIPC(EventHandler* handler);
@ -32,14 +34,16 @@ class IPC {
static std::filesystem::path socketFolder_; static std::filesystem::path socketFolder_;
private: private:
void startIPC(); void socketListener();
void parseIPC(const std::string&); void parseIPC(const std::string&);
std::thread ipcThread_;
std::mutex callbackMutex_; std::mutex callbackMutex_;
util::JsonParser parser_; util::JsonParser parser_;
std::list<std::pair<std::string, EventHandler*>> callbacks_; std::list<std::pair<std::string, EventHandler*>> callbacks_;
int socketfd_; // the hyprland socket file descriptor
bool running_ = true;
}; };
inline std::unique_ptr<IPC> gIPC;
inline bool modulesReady = false; inline bool modulesReady = false;
}; // namespace waybar::modules::hyprland }; // namespace waybar::modules::hyprland

View File

@ -37,6 +37,8 @@ class Language : public waybar::ALabel, public EventHandler {
util::JsonParser parser_; util::JsonParser parser_;
Layout layout_; Layout layout_;
IPC& m_ipc;
}; };
} // namespace waybar::modules::hyprland } // namespace waybar::modules::hyprland

View File

@ -28,6 +28,8 @@ class Submap : public waybar::ALabel, public EventHandler {
std::string submap_; std::string submap_;
bool always_on_ = false; bool always_on_ = false;
std::string default_submap_ = "Default"; std::string default_submap_ = "Default";
IPC& m_ipc;
}; };
} // namespace waybar::modules::hyprland } // namespace waybar::modules::hyprland

View File

@ -60,6 +60,8 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
bool swallowing_; bool swallowing_;
bool fullscreen_; bool fullscreen_;
bool focused_; bool focused_;
IPC& m_ipc;
}; };
} // namespace waybar::modules::hyprland } // namespace waybar::modules::hyprland

View File

@ -89,6 +89,7 @@ class Workspace {
void updateTaskbar(const std::string& workspace_icon); void updateTaskbar(const std::string& workspace_icon);
bool handleClick(const GdkEventButton* event_button, WindowAddress const& addr) const; bool handleClick(const GdkEventButton* event_button, WindowAddress const& addr) const;
IPC& m_ipc;
}; };
} // namespace waybar::modules::hyprland } // namespace waybar::modules::hyprland

View File

@ -174,6 +174,7 @@ class Workspaces : public AModule, public EventHandler {
std::mutex m_mutex; std::mutex m_mutex;
const Bar& m_bar; const Bar& m_bar;
Gtk::Box m_box; Gtk::Box m_box;
IPC& m_ipc;
}; };
} // namespace waybar::modules::hyprland } // namespace waybar::modules::hyprland

View File

@ -1,6 +1,6 @@
project( project(
'waybar', 'cpp', 'c', 'waybar', 'cpp', 'c',
version: '0.11.0', version: '0.12.0',
license: 'MIT', license: 'MIT',
meson_version: '>= 0.59.0', meson_version: '>= 0.59.0',
default_options : [ default_options : [

View File

@ -273,14 +273,18 @@ waybar::modules::Battery::getInfos() {
// Scale these by the voltage to get μW/μWh. // Scale these by the voltage to get μW/μWh.
uint32_t current_now = 0; uint32_t current_now = 0;
int32_t _current_now_int = 0;
bool current_now_exists = false; bool current_now_exists = false;
if (fs::exists(bat / "current_now")) { if (fs::exists(bat / "current_now")) {
current_now_exists = true; current_now_exists = true;
std::ifstream(bat / "current_now") >> current_now; std::ifstream(bat / "current_now") >> _current_now_int;
} else if (fs::exists(bat / "current_avg")) { } else if (fs::exists(bat / "current_avg")) {
current_now_exists = true; current_now_exists = true;
std::ifstream(bat / "current_avg") >> current_now; std::ifstream(bat / "current_avg") >> _current_now_int;
} }
// Documentation ABI allows a negative value when discharging, positive
// value when charging.
current_now = std::abs(_current_now_int);
if (fs::exists(bat / "time_to_empty_now")) { if (fs::exists(bat / "time_to_empty_now")) {
time_to_empty_now_exists = true; time_to_empty_now_exists = true;
@ -324,11 +328,15 @@ waybar::modules::Battery::getInfos() {
} }
uint32_t power_now = 0; uint32_t power_now = 0;
int32_t _power_now_int = 0;
bool power_now_exists = false; bool power_now_exists = false;
if (fs::exists(bat / "power_now")) { if (fs::exists(bat / "power_now")) {
power_now_exists = true; power_now_exists = true;
std::ifstream(bat / "power_now") >> power_now; std::ifstream(bat / "power_now") >> _power_now_int;
} }
// Some drivers (example: Qualcomm) exposes use a negative value when
// discharging, positive value when charging.
power_now = std::abs(_power_now_int);
uint32_t energy_now = 0; uint32_t energy_now = 0;
bool energy_now_exists = false; bool energy_now_exists = false;

View File

@ -11,7 +11,6 @@
#include <filesystem> #include <filesystem>
#include <string> #include <string>
#include <thread>
namespace waybar::modules::hyprland { namespace waybar::modules::hyprland {
@ -44,71 +43,96 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
return socketFolder_; return socketFolder_;
} }
void IPC::startIPC() { IPC::IPC() {
// will start IPC and relay events to parseIPC // will start IPC and relay events to parseIPC
ipcThread_ = std::thread([this]() { socketListener(); });
}
std::thread([&]() { IPC::~IPC() {
// check for hyprland running_ = false;
const char* his = getenv("HYPRLAND_INSTANCE_SIGNATURE"); spdlog::info("Hyprland IPC stopping...");
if (socketfd_ != -1) {
if (his == nullptr) { spdlog::trace("Shutting down socket");
spdlog::warn("Hyprland is not running, Hyprland IPC will not be available."); if (shutdown(socketfd_, SHUT_RDWR) == -1) {
return; spdlog::error("Hyprland IPC: Couldn't shutdown socket");
} }
spdlog::trace("Closing socket");
if (!modulesReady) return; if (close(socketfd_) == -1) {
spdlog::error("Hyprland IPC: Couldn't close socket");
spdlog::info("Hyprland IPC starting");
struct sockaddr_un addr;
int socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (socketfd == -1) {
spdlog::error("Hyprland IPC: socketfd failed");
return;
} }
}
ipcThread_.join();
}
addr.sun_family = AF_UNIX; IPC& IPC::inst() {
static IPC ipc;
return ipc;
}
auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock"; void IPC::socketListener() {
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); // check for hyprland
const char* his = getenv("HYPRLAND_INSTANCE_SIGNATURE");
addr.sun_path[sizeof(addr.sun_path) - 1] = 0; if (his == nullptr) {
spdlog::warn("Hyprland is not running, Hyprland IPC will not be available.");
return;
}
int l = sizeof(struct sockaddr_un); if (!modulesReady) return;
if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) { spdlog::info("Hyprland IPC starting");
spdlog::error("Hyprland IPC: Unable to connect?");
return;
}
auto* file = fdopen(socketfd, "r"); struct sockaddr_un addr;
socketfd_ = socket(AF_UNIX, SOCK_STREAM, 0);
while (true) { if (socketfd_ == -1) {
std::array<char, 1024> buffer; // Hyprland socket2 events are max 1024 bytes spdlog::error("Hyprland IPC: socketfd failed");
return;
}
auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file); addr.sun_family = AF_UNIX;
if (receivedCharPtr == nullptr) { auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock";
std::this_thread::sleep_for(std::chrono::milliseconds(1)); strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
continue;
}
std::string messageReceived(buffer.data()); addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n'));
spdlog::debug("hyprland IPC received {}", messageReceived);
try { int l = sizeof(struct sockaddr_un);
parseIPC(messageReceived);
} catch (std::exception& e) {
spdlog::warn("Failed to parse IPC message: {}, reason: {}", messageReceived, e.what());
} catch (...) {
throw;
}
if (connect(socketfd_, (struct sockaddr*)&addr, l) == -1) {
spdlog::error("Hyprland IPC: Unable to connect?");
return;
}
auto* file = fdopen(socketfd_, "r");
if (file == nullptr) {
spdlog::error("Hyprland IPC: Couldn't open file descriptor");
return;
}
while (running_) {
std::array<char, 1024> buffer; // Hyprland socket2 events are max 1024 bytes
auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file);
if (receivedCharPtr == nullptr) {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
} }
}).detach();
std::string messageReceived(buffer.data());
messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n'));
spdlog::debug("hyprland IPC received {}", messageReceived);
try {
parseIPC(messageReceived);
} catch (std::exception& e) {
spdlog::warn("Failed to parse IPC message: {}, reason: {}", messageReceived, e.what());
} catch (...) {
throw;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
spdlog::debug("Hyprland IPC stopped");
} }
void IPC::parseIPC(const std::string& ev) { void IPC::parseIPC(const std::string& ev) {

View File

@ -10,13 +10,9 @@
namespace waybar::modules::hyprland { namespace waybar::modules::hyprland {
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, true), bar_(bar) { : ALabel(config, "language", id, "{}", 0, true), bar_(bar), m_ipc(IPC::inst()) {
modulesReady = true; modulesReady = true;
if (!gIPC) {
gIPC = std::make_unique<IPC>();
}
// get the active layout when open // get the active layout when open
initLanguage(); initLanguage();
@ -24,11 +20,11 @@ Language::Language(const std::string& id, const Bar& bar, const Json::Value& con
update(); update();
// register for hyprland ipc // register for hyprland ipc
gIPC->registerForIPC("activelayout", this); m_ipc.registerForIPC("activelayout", this);
} }
Language::~Language() { Language::~Language() {
gIPC->unregisterForIPC(this); m_ipc.unregisterForIPC(this);
// wait for possible event handler to finish // wait for possible event handler to finish
std::lock_guard<std::mutex> lg(mutex_); std::lock_guard<std::mutex> lg(mutex_);
} }
@ -85,7 +81,7 @@ void Language::onEvent(const std::string& ev) {
} }
void Language::initLanguage() { void Language::initLanguage() {
const auto inputDevices = gIPC->getSocket1Reply("devices"); const auto inputDevices = m_ipc.getSocket1Reply("devices");
const auto kbName = config_["keyboard-name"].asString(); const auto kbName = config_["keyboard-name"].asString();

View File

@ -7,15 +7,11 @@
namespace waybar::modules::hyprland { namespace waybar::modules::hyprland {
Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "submap", id, "{}", 0, true), bar_(bar) { : ALabel(config, "submap", id, "{}", 0, true), bar_(bar), m_ipc(IPC::inst()) {
modulesReady = true; modulesReady = true;
parseConfig(config); parseConfig(config);
if (!gIPC) {
gIPC = std::make_unique<IPC>();
}
label_.hide(); label_.hide();
ALabel::update(); ALabel::update();
@ -27,12 +23,12 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config)
} }
// register for hyprland ipc // register for hyprland ipc
gIPC->registerForIPC("submap", this); m_ipc.registerForIPC("submap", this);
dp.emit(); dp.emit();
} }
Submap::~Submap() { Submap::~Submap() {
gIPC->unregisterForIPC(this); m_ipc.unregisterForIPC(this);
// wait for possible event handler to finish // wait for possible event handler to finish
std::lock_guard<std::mutex> lg(mutex_); std::lock_guard<std::mutex> lg(mutex_);
} }

View File

@ -7,7 +7,6 @@
#include <algorithm> #include <algorithm>
#include <shared_mutex> #include <shared_mutex>
#include <thread>
#include <vector> #include <vector>
#include "modules/hyprland/backend.hpp" #include "modules/hyprland/backend.hpp"
@ -19,22 +18,18 @@ namespace waybar::modules::hyprland {
std::shared_mutex windowIpcSmtx; std::shared_mutex windowIpcSmtx;
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) { : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar), m_ipc(IPC::inst()) {
std::unique_lock<std::shared_mutex> windowIpcUniqueLock(windowIpcSmtx); std::unique_lock<std::shared_mutex> windowIpcUniqueLock(windowIpcSmtx);
modulesReady = true; modulesReady = true;
separateOutputs_ = config["separate-outputs"].asBool(); separateOutputs_ = config["separate-outputs"].asBool();
if (!gIPC) {
gIPC = std::make_unique<IPC>();
}
// register for hyprland ipc // register for hyprland ipc
gIPC->registerForIPC("activewindow", this); m_ipc.registerForIPC("activewindow", this);
gIPC->registerForIPC("closewindow", this); m_ipc.registerForIPC("closewindow", this);
gIPC->registerForIPC("movewindow", this); m_ipc.registerForIPC("movewindow", this);
gIPC->registerForIPC("changefloatingmode", this); m_ipc.registerForIPC("changefloatingmode", this);
gIPC->registerForIPC("fullscreen", this); m_ipc.registerForIPC("fullscreen", this);
windowIpcUniqueLock.unlock(); windowIpcUniqueLock.unlock();
@ -45,7 +40,7 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
Window::~Window() { Window::~Window() {
std::unique_lock<std::shared_mutex> windowIpcUniqueLock(windowIpcSmtx); std::unique_lock<std::shared_mutex> windowIpcUniqueLock(windowIpcSmtx);
gIPC->unregisterForIPC(this); m_ipc.unregisterForIPC(this);
} }
auto Window::update() -> void { auto Window::update() -> void {
@ -76,11 +71,12 @@ auto Window::update() -> void {
tooltip_format = config_["tooltip-format"].asString(); tooltip_format = config_["tooltip-format"].asString();
} }
if (!tooltip_format.empty()) { if (!tooltip_format.empty()) {
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName), label_.set_tooltip_text(
fmt::arg("initialTitle", windowData_.initial_title), fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName),
fmt::arg("class", windowData_.class_name), fmt::arg("initialTitle", windowData_.initial_title),
fmt::arg("initialClass", windowData_.initial_class_name))); fmt::arg("class", windowData_.class_name),
} else if (!label_text.empty()){ fmt::arg("initialClass", windowData_.initial_class_name)));
} else if (!label_text.empty()) {
label_.set_tooltip_text(label_text); label_.set_tooltip_text(label_text);
} }
} }
@ -114,7 +110,7 @@ auto Window::update() -> void {
} }
auto Window::getActiveWorkspace() -> Workspace { auto Window::getActiveWorkspace() -> Workspace {
const auto workspace = gIPC->getSocket1JsonReply("activeworkspace"); const auto workspace = IPC::inst().getSocket1JsonReply("activeworkspace");
if (workspace.isObject()) { if (workspace.isObject()) {
return Workspace::parse(workspace); return Workspace::parse(workspace);
@ -124,24 +120,33 @@ auto Window::getActiveWorkspace() -> Workspace {
} }
auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
const auto monitors = gIPC->getSocket1JsonReply("monitors"); const auto monitors = IPC::inst().getSocket1JsonReply("monitors");
if (monitors.isArray()) { if (monitors.isArray()) {
auto monitor = std::find_if(monitors.begin(), monitors.end(), [&](Json::Value monitor) { auto monitor = std::ranges::find_if(
return monitor["name"] == monitorName; monitors, [&](Json::Value monitor) { return monitor["name"] == monitorName; });
});
if (monitor == std::end(monitors)) { if (monitor == std::end(monitors)) {
spdlog::warn("Monitor not found: {}", monitorName); spdlog::warn("Monitor not found: {}", monitorName);
return Workspace{-1, 0, "", ""}; return Workspace{
.id = -1,
.windows = 0,
.last_window = "",
.last_window_title = "",
};
} }
const int id = (*monitor)["activeWorkspace"]["id"].asInt(); const int id = (*monitor)["activeWorkspace"]["id"].asInt();
const auto workspaces = gIPC->getSocket1JsonReply("workspaces"); const auto workspaces = IPC::inst().getSocket1JsonReply("workspaces");
if (workspaces.isArray()) { if (workspaces.isArray()) {
auto workspace = std::find_if(workspaces.begin(), workspaces.end(), auto workspace = std::ranges::find_if(
[&](Json::Value workspace) { return workspace["id"] == id; }); workspaces, [&](Json::Value workspace) { return workspace["id"] == id; });
if (workspace == std::end(workspaces)) { if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id); spdlog::warn("No workspace with id {}", id);
return Workspace{-1, 0, "", ""}; return Workspace{
.id = -1,
.windows = 0,
.last_window = "",
.last_window_title = "",
};
} }
return Workspace::parse(*workspace); return Workspace::parse(*workspace);
}; };
@ -152,18 +157,22 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace {
return Workspace{ return Workspace{
value["id"].asInt(), .id = value["id"].asInt(),
value["windows"].asInt(), .windows = value["windows"].asInt(),
value["lastwindow"].asString(), .last_window = value["lastwindow"].asString(),
value["lastwindowtitle"].asString(), .last_window_title = value["lastwindowtitle"].asString(),
}; };
} }
auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData { auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData {
return WindowData{value["floating"].asBool(), value["monitor"].asInt(), return WindowData{.floating = value["floating"].asBool(),
value["class"].asString(), value["initialClass"].asString(), .monitor = value["monitor"].asInt(),
value["title"].asString(), value["initialTitle"].asString(), .class_name = value["class"].asString(),
value["fullscreen"].asBool(), !value["grouped"].empty()}; .initial_class_name = value["initialClass"].asString(),
.title = value["title"].asString(),
.initial_title = value["initialTitle"].asString(),
.fullscreen = value["fullscreen"].asBool(),
.grouped = !value["grouped"].empty()};
} }
void Window::queryActiveWorkspace() { void Window::queryActiveWorkspace() {
@ -177,11 +186,10 @@ void Window::queryActiveWorkspace() {
focused_ = true; focused_ = true;
if (workspace_.windows > 0) { if (workspace_.windows > 0) {
const auto clients = gIPC->getSocket1JsonReply("clients"); const auto clients = m_ipc.getSocket1JsonReply("clients");
if (clients.isArray()) { if (clients.isArray()) {
auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { auto activeWindow = std::ranges::find_if(
return window["address"] == workspace_.last_window; clients, [&](Json::Value window) { return window["address"] == workspace_.last_window; });
});
if (activeWindow == std::end(clients)) { if (activeWindow == std::end(clients)) {
focused_ = false; focused_ = false;
@ -191,22 +199,19 @@ void Window::queryActiveWorkspace() {
windowData_ = WindowData::parse(*activeWindow); windowData_ = WindowData::parse(*activeWindow);
updateAppIconName(windowData_.class_name, windowData_.initial_class_name); updateAppIconName(windowData_.class_name, windowData_.initial_class_name);
std::vector<Json::Value> workspaceWindows; std::vector<Json::Value> workspaceWindows;
std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows), std::ranges::copy_if(clients, std::back_inserter(workspaceWindows), [&](Json::Value window) {
[&](Json::Value window) { return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); });
}); swallowing_ = std::ranges::any_of(workspaceWindows, [&](Json::Value window) {
swallowing_ = return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) { });
return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
});
std::vector<Json::Value> visibleWindows; std::vector<Json::Value> visibleWindows;
std::copy_if(workspaceWindows.begin(), workspaceWindows.end(), std::ranges::copy_if(workspaceWindows, std::back_inserter(visibleWindows),
std::back_inserter(visibleWindows), [&](Json::Value window) { return !window["hidden"].asBool(); });
[&](Json::Value window) { return !window["hidden"].asBool(); });
solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(),
[&](Json::Value window) { return !window["floating"].asBool(); }); [&](Json::Value window) { return !window["floating"].asBool(); });
allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(), allFloating_ = std::ranges::all_of(
[&](Json::Value window) { return window["floating"].asBool(); }); visibleWindows, [&](Json::Value window) { return window["floating"].asBool(); });
fullscreen_ = windowData_.fullscreen; fullscreen_ = windowData_.fullscreen;
// Fullscreen windows look like they are solo // Fullscreen windows look like they are solo
@ -219,7 +224,7 @@ void Window::queryActiveWorkspace() {
} else { } else {
soloClass_ = ""; soloClass_ = "";
} }
}; }
} else { } else {
focused_ = false; focused_ = false;
windowData_ = WindowData{}; windowData_ = WindowData{};

View File

@ -20,7 +20,8 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma
m_windows(workspace_data["windows"].asInt()), m_windows(workspace_data["windows"].asInt()),
m_isActive(true), m_isActive(true),
m_isPersistentRule(workspace_data["persistent-rule"].asBool()), m_isPersistentRule(workspace_data["persistent-rule"].asBool()),
m_isPersistentConfig(workspace_data["persistent-config"].asBool()) { m_isPersistentConfig(workspace_data["persistent-config"].asBool()),
m_ipc(IPC::inst()) {
if (m_name.starts_with("name:")) { if (m_name.starts_with("name:")) {
m_name = m_name.substr(5); m_name = m_name.substr(5);
} else if (m_name.starts_with("special")) { } else if (m_name.starts_with("special")) {
@ -70,20 +71,20 @@ bool Workspace::handleClicked(GdkEventButton *bt) const {
try { try {
if (id() > 0) { // normal if (id() > 0) { // normal
if (m_workspaceManager.moveToMonitor()) { if (m_workspaceManager.moveToMonitor()) {
gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); m_ipc.getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id()));
} else { } else {
gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); m_ipc.getSocket1Reply("dispatch workspace " + std::to_string(id()));
} }
} else if (!isSpecial()) { // named (this includes persistent) } else if (!isSpecial()) { // named (this includes persistent)
if (m_workspaceManager.moveToMonitor()) { if (m_workspaceManager.moveToMonitor()) {
gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); m_ipc.getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name());
} else { } else {
gIPC->getSocket1Reply("dispatch workspace name:" + name()); m_ipc.getSocket1Reply("dispatch workspace name:" + name());
} }
} else if (id() != -99) { // named special } else if (id() != -99) { // named special
gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); m_ipc.getSocket1Reply("dispatch togglespecialworkspace " + name());
} else { // special } else { // special
gIPC->getSocket1Reply("dispatch togglespecialworkspace"); m_ipc.getSocket1Reply("dispatch togglespecialworkspace");
} }
return true; return true;
} catch (const std::exception &e) { } catch (const std::exception &e) {

View File

@ -14,7 +14,10 @@
namespace waybar::modules::hyprland { namespace waybar::modules::hyprland {
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), m_bar(bar), m_box(bar.orientation, 0) { : AModule(config, "workspaces", id, false, false),
m_bar(bar),
m_box(bar.orientation, 0),
m_ipc(IPC::inst()) {
modulesReady = true; modulesReady = true;
parseConfig(config); parseConfig(config);
@ -25,23 +28,19 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
m_box.get_style_context()->add_class(MODULE_CLASS); m_box.get_style_context()->add_class(MODULE_CLASS);
event_box_.add(m_box); event_box_.add(m_box);
if (!gIPC) {
gIPC = std::make_unique<IPC>();
}
setCurrentMonitorId(); setCurrentMonitorId();
init(); init();
registerIpc(); registerIpc();
} }
Workspaces::~Workspaces() { Workspaces::~Workspaces() {
gIPC->unregisterForIPC(this); m_ipc.unregisterForIPC(this);
// wait for possible event handler to finish // wait for possible event handler to finish
std::lock_guard<std::mutex> lg(m_mutex); std::lock_guard<std::mutex> lg(m_mutex);
} }
void Workspaces::init() { void Workspaces::init() {
m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); m_activeWorkspaceName = (m_ipc.getSocket1JsonReply("activeworkspace"))["name"].asString();
initializeWorkspaces(); initializeWorkspaces();
dp.emit(); dp.emit();
@ -162,7 +161,7 @@ std::string Workspaces::getRewrite(std::string window_class, std::string window_
std::vector<std::string> Workspaces::getVisibleWorkspaces() { std::vector<std::string> Workspaces::getVisibleWorkspaces() {
std::vector<std::string> visibleWorkspaces; std::vector<std::string> visibleWorkspaces;
auto monitors = gIPC->getSocket1JsonReply("monitors"); auto monitors = IPC::inst().getSocket1JsonReply("monitors");
for (const auto &monitor : monitors) { for (const auto &monitor : monitors) {
auto ws = monitor["activeWorkspace"]; auto ws = monitor["activeWorkspace"];
if (ws.isObject() && ws["name"].isString()) { if (ws.isObject() && ws["name"].isString()) {
@ -186,8 +185,8 @@ void Workspaces::initializeWorkspaces() {
} }
// get all current workspaces // get all current workspaces
auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces");
auto const clientsJson = gIPC->getSocket1JsonReply("clients"); auto const clientsJson = m_ipc.getSocket1JsonReply("clients");
for (Json::Value workspaceJson : workspacesJson) { for (Json::Value workspaceJson : workspacesJson) {
std::string workspaceName = workspaceJson["name"].asString(); std::string workspaceName = workspaceJson["name"].asString();
@ -286,7 +285,7 @@ void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJs
void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &clientsJson) { void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &clientsJson) {
spdlog::info("Loading persistent workspaces from Hyprland workspace rules"); spdlog::info("Loading persistent workspaces from Hyprland workspace rules");
auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); auto const workspaceRules = m_ipc.getSocket1JsonReply("workspacerules");
for (Json::Value const &rule : workspaceRules) { for (Json::Value const &rule : workspaceRules) {
if (!rule["workspaceString"].isString()) { if (!rule["workspaceString"].isString()) {
spdlog::warn("Workspace rules: invalid workspaceString, skipping: {}", rule); spdlog::warn("Workspace rules: invalid workspaceString, skipping: {}", rule);
@ -295,7 +294,8 @@ void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &c
if (!rule["persistent"].asBool()) { if (!rule["persistent"].asBool()) {
continue; continue;
} }
auto const &workspace = rule["workspaceString"].asString(); auto const &workspace = rule.isMember("defaultName") ? rule["defaultName"].asString()
: rule["workspaceString"].asString();
auto const &monitor = rule["monitor"].asString(); auto const &monitor = rule["monitor"].asString();
// create this workspace persistently if: // create this workspace persistently if:
// 1. the allOutputs config option is enabled // 1. the allOutputs config option is enabled
@ -366,17 +366,20 @@ void Workspaces::onWorkspaceDestroyed(std::string const &payload) {
void Workspaces::onWorkspaceCreated(std::string const &workspaceName, void Workspaces::onWorkspaceCreated(std::string const &workspaceName,
Json::Value const &clientsData) { Json::Value const &clientsData) {
spdlog::debug("Workspace created: {}", workspaceName); spdlog::debug("Workspace created: {}", workspaceName);
auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces");
if (!isWorkspaceIgnored(workspaceName)) { if (!isWorkspaceIgnored(workspaceName)) {
auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); auto const workspaceRules = m_ipc.getSocket1JsonReply("workspacerules");
for (Json::Value workspaceJson : workspacesJson) { for (Json::Value workspaceJson : workspacesJson) {
std::string name = workspaceJson["name"].asString(); std::string name = workspaceJson["name"].asString();
if (name == workspaceName) { if (name == workspaceName) {
if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) &&
(showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(workspaceName)) { (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(workspaceName)) {
for (Json::Value const &rule : workspaceRules) { for (Json::Value const &rule : workspaceRules) {
if (rule["workspaceString"].asString() == workspaceName) { auto ruleWorkspaceName = rule.isMember("defaultName")
? rule["defaultName"].asString()
: rule["workspaceString"].asString();
if (ruleWorkspaceName == workspaceName) {
workspaceJson["persistent-rule"] = rule["persistent"].asBool(); workspaceJson["persistent-rule"] = rule["persistent"].asBool();
break; break;
} }
@ -398,7 +401,7 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) {
spdlog::debug("Workspace moved: {}", payload); spdlog::debug("Workspace moved: {}", payload);
// Update active workspace // Update active workspace
m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); m_activeWorkspaceName = (m_ipc.getSocket1JsonReply("activeworkspace"))["name"].asString();
if (allOutputs()) return; if (allOutputs()) return;
@ -406,7 +409,7 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) {
std::string monitorName = payload.substr(payload.find(',') + 1); std::string monitorName = payload.substr(payload.find(',') + 1);
if (m_bar.output->name == monitorName) { if (m_bar.output->name == monitorName) {
Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); Json::Value clientsData = m_ipc.getSocket1JsonReply("clients");
onWorkspaceCreated(workspaceName, clientsData); onWorkspaceCreated(workspaceName, clientsData);
} else { } else {
spdlog::debug("Removing workspace because it was moved to another monitor: {}"); spdlog::debug("Removing workspace because it was moved to another monitor: {}");
@ -435,7 +438,7 @@ void Workspaces::onMonitorFocused(std::string const &payload) {
spdlog::trace("Monitor focused: {}", payload); spdlog::trace("Monitor focused: {}", payload);
m_activeWorkspaceName = payload.substr(payload.find(',') + 1); m_activeWorkspaceName = payload.substr(payload.find(',') + 1);
for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { for (Json::Value &monitor : m_ipc.getSocket1JsonReply("monitors")) {
if (monitor["name"].asString() == payload.substr(0, payload.find(','))) { if (monitor["name"].asString() == payload.substr(0, payload.find(','))) {
auto name = monitor["specialWorkspace"]["name"].asString(); auto name = monitor["specialWorkspace"]["name"].asString();
m_activeSpecialWorkspaceName = !name.starts_with("special:") ? name : name.substr(8); m_activeSpecialWorkspaceName = !name.starts_with("special:") ? name : name.substr(8);
@ -545,7 +548,7 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) {
} }
if (inserter.has_value()) { if (inserter.has_value()) {
Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); Json::Value clientsData = m_ipc.getSocket1JsonReply("clients");
std::string jsonWindowAddress = fmt::format("0x{}", payload); std::string jsonWindowAddress = fmt::format("0x{}", payload);
auto client = std::ranges::find_if(clientsData, [&jsonWindowAddress](auto &c) { auto client = std::ranges::find_if(clientsData, [&jsonWindowAddress](auto &c) {
@ -718,24 +721,24 @@ void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payloa
} }
auto Workspaces::registerIpc() -> void { auto Workspaces::registerIpc() -> void {
gIPC->registerForIPC("workspace", this); m_ipc.registerForIPC("workspace", this);
gIPC->registerForIPC("activespecial", this); m_ipc.registerForIPC("activespecial", this);
gIPC->registerForIPC("createworkspace", this); m_ipc.registerForIPC("createworkspace", this);
gIPC->registerForIPC("destroyworkspace", this); m_ipc.registerForIPC("destroyworkspace", this);
gIPC->registerForIPC("focusedmon", this); m_ipc.registerForIPC("focusedmon", this);
gIPC->registerForIPC("moveworkspace", this); m_ipc.registerForIPC("moveworkspace", this);
gIPC->registerForIPC("renameworkspace", this); m_ipc.registerForIPC("renameworkspace", this);
gIPC->registerForIPC("openwindow", this); m_ipc.registerForIPC("openwindow", this);
gIPC->registerForIPC("closewindow", this); m_ipc.registerForIPC("closewindow", this);
gIPC->registerForIPC("movewindow", this); m_ipc.registerForIPC("movewindow", this);
gIPC->registerForIPC("urgent", this); m_ipc.registerForIPC("urgent", this);
gIPC->registerForIPC("configreloaded", this); m_ipc.registerForIPC("configreloaded", this);
if (windowRewriteConfigUsesTitle() || m_taskbarWithTitle) { if (windowRewriteConfigUsesTitle() || m_taskbarWithTitle) {
spdlog::info( spdlog::info(
"Registering for Hyprland's 'windowtitle' events because a user-defined window " "Registering for Hyprland's 'windowtitle' events because a user-defined window "
"rewrite rule uses the 'title' field."); "rewrite rule uses the 'title' field.");
gIPC->registerForIPC("windowtitle", this); m_ipc.registerForIPC("windowtitle", this);
} }
} }
@ -770,7 +773,7 @@ void Workspaces::removeWorkspace(std::string const &name) {
void Workspaces::setCurrentMonitorId() { void Workspaces::setCurrentMonitorId() {
// get monitor ID from name (used by persistent workspaces) // get monitor ID from name (used by persistent workspaces)
m_monitorId = 0; m_monitorId = 0;
auto monitors = gIPC->getSocket1JsonReply("monitors"); auto monitors = m_ipc.getSocket1JsonReply("monitors");
auto currentMonitor = std::find_if( auto currentMonitor = std::find_if(
monitors.begin(), monitors.end(), monitors.begin(), monitors.end(),
[this](const Json::Value &m) { return m["name"].asString() == m_bar.output->name; }); [this](const Json::Value &m) { return m["name"].asString() == m_bar.output->name; });
@ -846,7 +849,7 @@ void Workspaces::sortWorkspaces() {
} }
void Workspaces::setUrgentWorkspace(std::string const &windowaddress) { void Workspaces::setUrgentWorkspace(std::string const &windowaddress) {
const Json::Value clientsJson = gIPC->getSocket1JsonReply("clients"); const Json::Value clientsJson = m_ipc.getSocket1JsonReply("clients");
int workspaceId = -1; int workspaceId = -1;
for (Json::Value clientJson : clientsJson) { for (Json::Value clientJson : clientsJson) {
@ -870,7 +873,7 @@ auto Workspaces::update() -> void {
} }
void Workspaces::updateWindowCount() { void Workspaces::updateWindowCount() {
const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); const Json::Value workspacesJson = m_ipc.getSocket1JsonReply("workspaces");
for (auto &workspace : m_workspaces) { for (auto &workspace : m_workspaces) {
auto workspaceJson = auto workspaceJson =
std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) { std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) {
@ -917,11 +920,11 @@ bool Workspaces::updateWindowsToCreate() {
void Workspaces::updateWorkspaceStates() { void Workspaces::updateWorkspaceStates() {
const std::vector<std::string> visibleWorkspaces = getVisibleWorkspaces(); const std::vector<std::string> visibleWorkspaces = getVisibleWorkspaces();
auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces"); auto updatedWorkspaces = m_ipc.getSocket1JsonReply("workspaces");
for (auto &workspace : m_workspaces) { for (auto &workspace : m_workspaces) {
workspace->setActive(workspace->name() == m_activeWorkspaceName || workspace->setActive(workspace->name() == m_activeWorkspaceName ||
workspace->name() == m_activeSpecialWorkspaceName); workspace->name() == m_activeSpecialWorkspaceName);
if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { if (workspace->isActive() && workspace->isUrgent()) {
workspace->setUrgent(false); workspace->setUrgent(false);
} }
workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(),

View File

@ -9,7 +9,7 @@
namespace waybar::modules::niri { 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, true), bar_(bar) { : ALabel(config, "language", id, "{}", 0, false), bar_(bar) {
label_.hide(); label_.hide();
if (!gIPC) gIPC = std::make_unique<IPC>(); if (!gIPC) gIPC = std::make_unique<IPC>();