Hyprland IPC improvements
Fixes IPC being blocked at shutdown
This commit is contained in:
@ -5,6 +5,7 @@
|
|||||||
#include <memory>
|
#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 +20,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 +35,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::jthread 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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -83,6 +83,7 @@ class Workspace {
|
|||||||
Gtk::Button m_button;
|
Gtk::Button m_button;
|
||||||
Gtk::Box m_content;
|
Gtk::Box m_content;
|
||||||
Gtk::Label m_label;
|
Gtk::Label m_label;
|
||||||
|
IPC& m_ipc;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::hyprland
|
} // namespace waybar::modules::hyprland
|
||||||
|
@ -150,6 +150,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
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
@ -44,10 +43,33 @@ 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::jthread([this]() { socketListener(); });
|
||||||
|
}
|
||||||
|
|
||||||
std::thread([&]() {
|
IPC::~IPC() {
|
||||||
|
running_ = false;
|
||||||
|
spdlog::info("Hyprland IPC stopping...");
|
||||||
|
if (socketfd_ != -1) {
|
||||||
|
spdlog::trace("Shutting down socket");
|
||||||
|
if (shutdown(socketfd_, SHUT_RDWR) == -1) {
|
||||||
|
spdlog::error("Hyprland IPC: Couldn't shutdown socket");
|
||||||
|
}
|
||||||
|
spdlog::trace("Closing socket");
|
||||||
|
if (close(socketfd_) == -1) {
|
||||||
|
spdlog::error("Hyprland IPC: Couldn't close socket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipcThread_.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC& IPC::inst() {
|
||||||
|
static IPC ipc;
|
||||||
|
return ipc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPC::socketListener() {
|
||||||
// check for hyprland
|
// check for hyprland
|
||||||
const char* his = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
const char* his = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||||
|
|
||||||
@ -61,9 +83,9 @@ void IPC::startIPC() {
|
|||||||
spdlog::info("Hyprland IPC starting");
|
spdlog::info("Hyprland IPC starting");
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
int socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
socketfd_ = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
|
||||||
if (socketfd == -1) {
|
if (socketfd_ == -1) {
|
||||||
spdlog::error("Hyprland IPC: socketfd failed");
|
spdlog::error("Hyprland IPC: socketfd failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -77,14 +99,16 @@ void IPC::startIPC() {
|
|||||||
|
|
||||||
int l = sizeof(struct sockaddr_un);
|
int l = sizeof(struct sockaddr_un);
|
||||||
|
|
||||||
if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) {
|
if (connect(socketfd_, (struct sockaddr*)&addr, l) == -1) {
|
||||||
spdlog::error("Hyprland IPC: Unable to connect?");
|
spdlog::error("Hyprland IPC: Unable to connect?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto* file = fdopen(socketfd_, "r");
|
||||||
auto* file = fdopen(socketfd, "r");
|
if (file == nullptr) {
|
||||||
|
spdlog::error("Hyprland IPC: Couldn't open file descriptor");
|
||||||
while (true) {
|
return;
|
||||||
|
}
|
||||||
|
while (running_) {
|
||||||
std::array<char, 1024> buffer; // Hyprland socket2 events are max 1024 bytes
|
std::array<char, 1024> buffer; // Hyprland socket2 events are max 1024 bytes
|
||||||
|
|
||||||
auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file);
|
auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file);
|
||||||
@ -108,7 +132,7 @@ void IPC::startIPC() {
|
|||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
}).detach();
|
spdlog::debug("Hyprland IPC stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
void IPC::parseIPC(const std::string& ev) {
|
void IPC::parseIPC(const std::string& ev) {
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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_);
|
||||||
}
|
}
|
||||||
|
@ -19,22 +19,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 +41,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 {
|
||||||
@ -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_ =
|
swallowing_ = std::ranges::any_of(workspaceWindows, [&](Json::Value window) {
|
||||||
std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) {
|
|
||||||
return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
|
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{};
|
||||||
|
@ -18,7 +18,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")) {
|
||||||
@ -58,20 +59,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) {
|
||||||
|
@ -13,7 +13,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);
|
||||||
|
|
||||||
@ -24,23 +27,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();
|
||||||
@ -161,7 +160,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()) {
|
||||||
@ -185,8 +184,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();
|
||||||
@ -285,7 +284,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);
|
||||||
@ -365,10 +364,10 @@ 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) {
|
||||||
@ -397,7 +396,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;
|
||||||
|
|
||||||
@ -405,7 +404,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: {}");
|
||||||
@ -434,7 +433,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);
|
||||||
@ -542,7 +541,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 =
|
auto client =
|
||||||
@ -660,24 +659,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()) {
|
if (windowRewriteConfigUsesTitle()) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,7 +711,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; });
|
||||||
@ -788,7 +787,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) {
|
||||||
@ -812,7 +811,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) {
|
||||||
@ -859,7 +858,7 @@ 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);
|
||||||
|
Reference in New Issue
Block a user