use hyprland command socket instead of spawning a hyprctl process

This commit is contained in:
2026-02-07 15:31:23 +01:00
parent d9ac353a0d
commit ff2d0afd9b
8 changed files with 153 additions and 70 deletions

View File

@@ -1,52 +1,15 @@
#pragma once
#include <algorithm>
#include <array>
#include <cassert>
#include <cstring>
#include <span>
#include <nlohmann/json.hpp>
#include <string>
#include <sys/socket.h>
#include "helpers/command.hpp"
class HyprctlHelper {
public:
static nlohmann::json getMonitorData() {
std::string result = CommandHelper::exec("hyprctl -j monitors");
assert(!result.empty() && "Failed to get monitor data from hyprctl");
auto json = nlohmann::json::parse(result);
assert(json.is_array());
return json;
}
static nlohmann::json getWorkspaceData() {
std::string result = CommandHelper::exec("hyprctl -j workspaces");
assert(!result.empty() && "Failed to get workspace data from hyprctl");
auto json = nlohmann::json::parse(result);
assert(json.is_array());
return json;
}
static nlohmann::json getClientData() {
std::string result = CommandHelper::exec("hyprctl -j clients");
assert(!result.empty() && "Failed to get client data from hyprctl");
auto json = nlohmann::json::parse(result);
assert(json.is_array());
return json;
}
static void dispatchWorkspace(int workspaceNumber) {
std::string out = "hyprctl dispatch workspace " + std::to_string(workspaceNumber);
CommandHelper::execNoOutput(out.c_str());
}
};
#include <sys/un.h>
#include <unistd.h>
class HyprSocketHelper {
public:
@@ -66,4 +29,105 @@ class HyprSocketHelper {
return std::string();
}
static std::string getHyprlandCommandSocketPath() {
const char *hyprlandInstanceSignature = std::getenv("HYPRLAND_INSTANCE_SIGNATURE");
const char *xdgRuntimeDir = std::getenv("XDG_RUNTIME_DIR");
if (!xdgRuntimeDir || !hyprlandInstanceSignature) {
return std::string();
}
std::string basePath = std::string(xdgRuntimeDir) + "/hypr/" + std::string(hyprlandInstanceSignature) + "/";
std::string sock1 = basePath + ".socket.sock";
if (access(sock1.c_str(), F_OK) == 0) {
return sock1;
}
return std::string();
}
};
class HyprctlHelper {
public:
static std::string sendCommand(const std::string &command) {
std::string socketPath = HyprSocketHelper::getHyprlandCommandSocketPath();
assert(!socketPath.empty() && "Failed to get Hyprland command socket path");
int socketFd = socket(AF_UNIX, SOCK_STREAM, 0);
assert(socketFd != -1 && "Failed to create Hyprland command socket");
struct sockaddr_un addr {};
std::memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
auto sunPathSpan = std::span<char, sizeof(addr.sun_path)>(addr.sun_path);
std::ranges::fill(sunPathSpan, '\0');
constexpr size_t kMaxPathLength = sizeof(addr.sun_path) - 1;
const size_t copyLength = std::min(socketPath.size(), kMaxPathLength);
std::copy_n(socketPath.begin(), copyLength, sunPathSpan.begin());
auto *sockaddrPtr = static_cast<struct sockaddr *>(static_cast<void *>(&addr));
int result = connect(socketFd, sockaddrPtr, sizeof(addr));
assert(result != -1 && "Failed to connect to Hyprland command socket");
ssize_t bytesSent = send(socketFd, command.c_str(), command.size(), 0);
assert(bytesSent != -1 && "Failed to send command to Hyprland socket");
shutdown(socketFd, SHUT_WR);
std::string response;
constexpr size_t kBufferSize = 4096;
std::array<char, kBufferSize> buffer {};
while (true) {
ssize_t bytesRead = recv(socketFd, buffer.data(), buffer.size(), 0);
if (bytesRead <= 0) {
break;
}
response.append(buffer.data(), static_cast<size_t>(bytesRead));
}
close(socketFd);
return response;
}
static nlohmann::json getMonitorData() {
std::string result = sendCommand("j/monitors");
assert(!result.empty() && "Failed to get monitor data from Hyprland socket");
auto json = nlohmann::json::parse(result);
assert(json.is_array());
return json;
}
static nlohmann::json getWorkspaceData() {
std::string result = sendCommand("j/workspaces");
assert(!result.empty() && "Failed to get workspace data from Hyprland socket");
auto json = nlohmann::json::parse(result);
assert(json.is_array());
return json;
}
static nlohmann::json getClientData() {
std::string result = sendCommand("j/clients");
assert(!result.empty() && "Failed to get client data from Hyprland socket");
auto json = nlohmann::json::parse(result);
assert(json.is_array());
return json;
}
static void dispatchWorkspace(int workspaceNumber) {
std::string command = "dispatch workspace " + std::to_string(workspaceNumber);
sendCommand(command);
}
};

View File

@@ -17,14 +17,29 @@ class BaseNotification : public Gtk::Window {
void resumeAutoClose();
void startAutoClose(int timeoutMs);
sigc::signal<void(uint64_t)> signal_close;
sigc::signal<void(bool)> signal_hover_changed;
uint64_t getNotificationId() const {
return this->notificationId;
}
sigc::signal<void(uint64_t)> getSignalClose() {
return this->signalClose;
}
sigc::signal<void(bool)> getSignalHoverChanged() {
return this->signalHoverChanged;
}
private:
sigc::signal<void(uint64_t)> signalClose;
sigc::signal<void(bool)> signalHoverChanged;
uint64_t notificationId;
bool autoClosePaused = false;
int autoCloseRemainingMs = 0;
std::chrono::steady_clock::time_point autoCloseDeadline;
sigc::connection autoCloseConnection;
void ensure_notification_css_loaded();
void start_auto_close_timeout(int timeoutMs);
@@ -32,10 +47,15 @@ class BaseNotification : public Gtk::Window {
void resume_auto_close();
protected:
uint64_t notificationId;
bool getAutoClosePaused() const {
return this->autoClosePaused;
}
bool autoClosePaused = false;
int autoCloseRemainingMs = 0;
std::chrono::steady_clock::time_point autoCloseDeadline;
sigc::connection autoCloseConnection;
int getAutoCloseRemainingMs() const {
return this->autoCloseRemainingMs;
}
std::chrono::steady_clock::time_point getAutoCloseDeadline() const {
return this->autoCloseDeadline;
}
};