Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hyprland IPC improvements #3945

Merged
merged 3 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions include/modules/hyprland/backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

#include <filesystem>
#include <list>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <utility>

#include "util/json.hpp"
Expand All @@ -19,7 +19,9 @@ class EventHandler {

class IPC {
public:
IPC() { startIPC(); }
IPC();
~IPC();
static IPC& inst();

void registerForIPC(const std::string& ev, EventHandler* ev_handler);
void unregisterForIPC(EventHandler* handler);
Expand All @@ -32,14 +34,16 @@ class IPC {
static std::filesystem::path socketFolder_;

private:
void startIPC();
void socketListener();
void parseIPC(const std::string&);

std::thread ipcThread_;
std::mutex callbackMutex_;
util::JsonParser parser_;
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;
}; // namespace waybar::modules::hyprland
2 changes: 2 additions & 0 deletions include/modules/hyprland/language.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Language : public waybar::ALabel, public EventHandler {
util::JsonParser parser_;

Layout layout_;

IPC& m_ipc;
};

} // namespace waybar::modules::hyprland
2 changes: 2 additions & 0 deletions include/modules/hyprland/submap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class Submap : public waybar::ALabel, public EventHandler {
std::string submap_;
bool always_on_ = false;
std::string default_submap_ = "Default";

IPC& m_ipc;
};

} // namespace waybar::modules::hyprland
2 changes: 2 additions & 0 deletions include/modules/hyprland/window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
bool swallowing_;
bool fullscreen_;
bool focused_;

IPC& m_ipc;
};

} // namespace waybar::modules::hyprland
1 change: 1 addition & 0 deletions include/modules/hyprland/workspace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class Workspace {
Gtk::Button m_button;
Gtk::Box m_content;
Gtk::Label m_label;
IPC& m_ipc;
};

} // namespace waybar::modules::hyprland
1 change: 1 addition & 0 deletions include/modules/hyprland/workspaces.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class Workspaces : public AModule, public EventHandler {
std::mutex m_mutex;
const Bar& m_bar;
Gtk::Box m_box;
IPC& m_ipc;
};

} // namespace waybar::modules::hyprland
116 changes: 70 additions & 46 deletions src/modules/hyprland/backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include <filesystem>
#include <string>
#include <thread>

namespace waybar::modules::hyprland {

Expand Down Expand Up @@ -44,71 +43,96 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
return socketFolder_;
}

void IPC::startIPC() {
IPC::IPC() {
// will start IPC and relay events to parseIPC
ipcThread_ = std::thread([this]() { socketListener(); });
}

std::thread([&]() {
// check for hyprland
const char* his = getenv("HYPRLAND_INSTANCE_SIGNATURE");

if (his == nullptr) {
spdlog::warn("Hyprland is not running, Hyprland IPC will not be available.");
return;
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();
}

if (!modulesReady) return;
IPC& IPC::inst() {
static IPC ipc;
return ipc;
}

spdlog::info("Hyprland IPC starting");
void IPC::socketListener() {
// check for hyprland
const char* his = getenv("HYPRLAND_INSTANCE_SIGNATURE");

struct sockaddr_un addr;
int socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (his == nullptr) {
spdlog::warn("Hyprland is not running, Hyprland IPC will not be available.");
return;
}

if (socketfd == -1) {
spdlog::error("Hyprland IPC: socketfd failed");
return;
}
if (!modulesReady) return;

addr.sun_family = AF_UNIX;
spdlog::info("Hyprland IPC starting");

auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock";
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
struct sockaddr_un addr;
socketfd_ = socket(AF_UNIX, SOCK_STREAM, 0);

addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
if (socketfd_ == -1) {
spdlog::error("Hyprland IPC: socketfd failed");
return;
}

int l = sizeof(struct sockaddr_un);
addr.sun_family = AF_UNIX;

if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) {
spdlog::error("Hyprland IPC: Unable to connect?");
return;
}
auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock";
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);

auto* file = fdopen(socketfd, "r");
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;

while (true) {
std::array<char, 1024> buffer; // Hyprland socket2 events are max 1024 bytes
int l = sizeof(struct sockaddr_un);

auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file);
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

if (receivedCharPtr == nullptr) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file);

std::string messageReceived(buffer.data());
messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n'));
spdlog::debug("hyprland IPC received {}", messageReceived);
if (receivedCharPtr == nullptr) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}

try {
parseIPC(messageReceived);
} catch (std::exception& e) {
spdlog::warn("Failed to parse IPC message: {}, reason: {}", messageReceived, e.what());
} catch (...) {
throw;
}
std::string messageReceived(buffer.data());
messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n'));
spdlog::debug("hyprland IPC received {}", messageReceived);

std::this_thread::sleep_for(std::chrono::milliseconds(1));
try {
parseIPC(messageReceived);
} catch (std::exception& e) {
spdlog::warn("Failed to parse IPC message: {}, reason: {}", messageReceived, e.what());
} catch (...) {
throw;
}
}).detach();

std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
spdlog::debug("Hyprland IPC stopped");
}

void IPC::parseIPC(const std::string& ev) {
Expand Down
12 changes: 4 additions & 8 deletions src/modules/hyprland/language.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,21 @@
namespace waybar::modules::hyprland {

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;

if (!gIPC) {
gIPC = std::make_unique<IPC>();
}

// get the active layout when open
initLanguage();

label_.hide();
update();

// register for hyprland ipc
gIPC->registerForIPC("activelayout", this);
m_ipc.registerForIPC("activelayout", this);
}

Language::~Language() {
gIPC->unregisterForIPC(this);
m_ipc.unregisterForIPC(this);
// wait for possible event handler to finish
std::lock_guard<std::mutex> lg(mutex_);
}
Expand Down Expand Up @@ -85,7 +81,7 @@ void Language::onEvent(const std::string& ev) {
}

void Language::initLanguage() {
const auto inputDevices = gIPC->getSocket1Reply("devices");
const auto inputDevices = m_ipc.getSocket1Reply("devices");

const auto kbName = config_["keyboard-name"].asString();

Expand Down
10 changes: 3 additions & 7 deletions src/modules/hyprland/submap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,11 @@
namespace waybar::modules::hyprland {

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;

parseConfig(config);

if (!gIPC) {
gIPC = std::make_unique<IPC>();
}

label_.hide();
ALabel::update();

Expand All @@ -27,12 +23,12 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config)
}

// register for hyprland ipc
gIPC->registerForIPC("submap", this);
m_ipc.registerForIPC("submap", this);
dp.emit();
}

Submap::~Submap() {
gIPC->unregisterForIPC(this);
m_ipc.unregisterForIPC(this);
// wait for possible event handler to finish
std::lock_guard<std::mutex> lg(mutex_);
}
Expand Down
Loading