Skip to content

Commit

Permalink
Merge pull request #3945 from zjeffer/fix/zjeffer/hyprland-ipc
Browse files Browse the repository at this point in the history
Hyprland IPC improvements
  • Loading branch information
Alexays authored Feb 20, 2025
2 parents d098dbb + 37c6cd4 commit 0d8d425
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 159 deletions.
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

0 comments on commit 0d8d425

Please sign in to comment.