use hyprland command socket instead of spawning a hyprctl process
This commit is contained in:
@@ -1,52 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <span>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
#include "helpers/command.hpp"
|
#include <unistd.h>
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class HyprSocketHelper {
|
class HyprSocketHelper {
|
||||||
public:
|
public:
|
||||||
@@ -66,4 +29,105 @@ class HyprSocketHelper {
|
|||||||
|
|
||||||
return std::string();
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@@ -17,14 +17,29 @@ class BaseNotification : public Gtk::Window {
|
|||||||
void resumeAutoClose();
|
void resumeAutoClose();
|
||||||
void startAutoClose(int timeoutMs);
|
void startAutoClose(int timeoutMs);
|
||||||
|
|
||||||
sigc::signal<void(uint64_t)> signal_close;
|
|
||||||
sigc::signal<void(bool)> signal_hover_changed;
|
|
||||||
|
|
||||||
uint64_t getNotificationId() const {
|
uint64_t getNotificationId() const {
|
||||||
return this->notificationId;
|
return this->notificationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sigc::signal<void(uint64_t)> getSignalClose() {
|
||||||
|
return this->signalClose;
|
||||||
|
}
|
||||||
|
|
||||||
|
sigc::signal<void(bool)> getSignalHoverChanged() {
|
||||||
|
return this->signalHoverChanged;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
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 ensure_notification_css_loaded();
|
||||||
|
|
||||||
void start_auto_close_timeout(int timeoutMs);
|
void start_auto_close_timeout(int timeoutMs);
|
||||||
@@ -32,10 +47,15 @@ class BaseNotification : public Gtk::Window {
|
|||||||
void resume_auto_close();
|
void resume_auto_close();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint64_t notificationId;
|
bool getAutoClosePaused() const {
|
||||||
|
return this->autoClosePaused;
|
||||||
|
}
|
||||||
|
|
||||||
bool autoClosePaused = false;
|
int getAutoCloseRemainingMs() const {
|
||||||
int autoCloseRemainingMs = 0;
|
return this->autoCloseRemainingMs;
|
||||||
std::chrono::steady_clock::time_point autoCloseDeadline;
|
}
|
||||||
sigc::connection autoCloseConnection;
|
|
||||||
|
std::chrono::steady_clock::time_point getAutoCloseDeadline() const {
|
||||||
|
return this->autoCloseDeadline;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@@ -92,7 +92,7 @@ void HyprlandService::bindHyprlandSocket() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr{};
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
std::strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
|
std::strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
|
||||||
|
|||||||
@@ -52,11 +52,11 @@ void NotificationController::showNotificationOnAllMonitors(NotifyMessage notify)
|
|||||||
timeout = DEFAULT_NOTIFICATION_TIMEOUT;
|
timeout = DEFAULT_NOTIFICATION_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
notification->signal_close.connect([this, id = notification->getNotificationId()](int) {
|
notification->getSignalClose().connect([this, id = notification->getNotificationId()](int) {
|
||||||
closeNotification(id);
|
closeNotification(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
notification->signal_hover_changed.connect([this, id = notification->getNotificationId()](bool hovered) {
|
notification->getSignalHoverChanged().connect([this, id = notification->getNotificationId()](bool hovered) {
|
||||||
updateHoverState(id, hovered);
|
updateHoverState(id, hovered);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -80,11 +80,11 @@ void NotificationController::showCopyNotification(NotifyMessage notify) {
|
|||||||
notification->show();
|
notification->show();
|
||||||
notifications.push_back(notification);
|
notifications.push_back(notification);
|
||||||
|
|
||||||
notification->signal_close.connect([this, id = notification->getNotificationId()](int) {
|
notification->getSignalClose().connect([this, id = notification->getNotificationId()](int) {
|
||||||
closeNotification(id);
|
closeNotification(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
notification->signal_hover_changed.connect([this, id = notification->getNotificationId()](bool hovered) {
|
notification->getSignalHoverChanged().connect([this, id = notification->getNotificationId()](bool hovered) {
|
||||||
updateHoverState(id, hovered);
|
updateHoverState(id, hovered);
|
||||||
});
|
});
|
||||||
notification->startAutoClose(DEFAULT_NOTIFICATION_TIMEOUT);
|
notification->startAutoClose(DEFAULT_NOTIFICATION_TIMEOUT);
|
||||||
@@ -103,11 +103,11 @@ void NotificationController::showSpotifyNotification(MprisPlayer2Message mpris)
|
|||||||
notification->show();
|
notification->show();
|
||||||
notifications.push_back(notification);
|
notifications.push_back(notification);
|
||||||
|
|
||||||
notification->signal_close.connect([this, id = notification->getNotificationId()](int) {
|
notification->getSignalClose().connect([this, id = notification->getNotificationId()](int) {
|
||||||
closeNotification(id);
|
closeNotification(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
notification->signal_hover_changed.connect([this, id = notification->getNotificationId()](bool hovered) {
|
notification->getSignalHoverChanged().connect([this, id = notification->getNotificationId()](bool hovered) {
|
||||||
updateHoverState(id, hovered);
|
updateHoverState(id, hovered);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -41,17 +41,17 @@ BaseNotification::BaseNotification(uint64_t notificationId, std::shared_ptr<Gdk:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->signal_close.emit(this->notificationId);
|
this->getSignalClose().emit(this->notificationId);
|
||||||
});
|
});
|
||||||
add_controller(window_click);
|
add_controller(window_click);
|
||||||
|
|
||||||
auto window_motion = Gtk::EventControllerMotion::create();
|
auto window_motion = Gtk::EventControllerMotion::create();
|
||||||
window_motion->signal_enter().connect([this](double, double) {
|
window_motion->signal_enter().connect([this](double, double) {
|
||||||
signal_hover_changed.emit(true);
|
getSignalHoverChanged().emit(true);
|
||||||
pause_auto_close();
|
pause_auto_close();
|
||||||
});
|
});
|
||||||
window_motion->signal_leave().connect([this]() {
|
window_motion->signal_leave().connect([this]() {
|
||||||
signal_hover_changed.emit(false);
|
getSignalHoverChanged().emit(false);
|
||||||
resume_auto_close();
|
resume_auto_close();
|
||||||
});
|
});
|
||||||
add_controller(window_motion);
|
add_controller(window_motion);
|
||||||
@@ -82,7 +82,7 @@ void BaseNotification::start_auto_close_timeout(int timeoutMs) {
|
|||||||
|
|
||||||
autoCloseDeadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs);
|
autoCloseDeadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs);
|
||||||
autoCloseConnection = Glib::signal_timeout().connect([this]() {
|
autoCloseConnection = Glib::signal_timeout().connect([this]() {
|
||||||
this->signal_close.emit(this->notificationId);
|
this->getSignalClose().emit(this->notificationId);
|
||||||
return false; // Don't repeat
|
return false; // Don't repeat
|
||||||
},
|
},
|
||||||
timeoutMs);
|
timeoutMs);
|
||||||
@@ -110,7 +110,7 @@ void BaseNotification::resume_auto_close() {
|
|||||||
|
|
||||||
autoClosePaused = false;
|
autoClosePaused = false;
|
||||||
if (autoCloseRemainingMs <= 0) {
|
if (autoCloseRemainingMs <= 0) {
|
||||||
this->signal_close.emit(this->notificationId);
|
this->getSignalClose().emit(this->notificationId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ void CopyNotification::createImageNotification(NotifyMessage notify) {
|
|||||||
copyToClipboard(this->copiedImage);
|
copyToClipboard(this->copiedImage);
|
||||||
spdlog::info("Copied image to clipboard");
|
spdlog::info("Copied image to clipboard");
|
||||||
|
|
||||||
this->signal_close.emit(this->notificationId);
|
this->getSignalClose().emit(this->getNotificationId());
|
||||||
});
|
});
|
||||||
saveToClipboardButton->set_tooltip_text("Copy to Clipboard");
|
saveToClipboardButton->set_tooltip_text("Copy to Clipboard");
|
||||||
saveToClipboardButton->add_css_class("notification-button");
|
saveToClipboardButton->add_css_class("notification-button");
|
||||||
@@ -100,7 +100,6 @@ void CopyNotification::createImageNotification(NotifyMessage notify) {
|
|||||||
|
|
||||||
auto saveToFileButton = Gtk::make_managed<IconButton>(Icon::SAVE);
|
auto saveToFileButton = Gtk::make_managed<IconButton>(Icon::SAVE);
|
||||||
saveToFileButton->signal_clicked().connect([this]() {
|
saveToFileButton->signal_clicked().connect([this]() {
|
||||||
// xdg-pic/screenshot // use env
|
|
||||||
auto xdgPicturesDir = Glib::get_user_special_dir(Glib::UserDirectory::PICTURES);
|
auto xdgPicturesDir = Glib::get_user_special_dir(Glib::UserDirectory::PICTURES);
|
||||||
auto dateStamp = Glib::DateTime::create_now_local().format("%Y%m%d_%H%M%S");
|
auto dateStamp = Glib::DateTime::create_now_local().format("%Y%m%d_%H%M%S");
|
||||||
auto filepath = xdgPicturesDir + "/screenshot";
|
auto filepath = xdgPicturesDir + "/screenshot";
|
||||||
@@ -108,7 +107,7 @@ void CopyNotification::createImageNotification(NotifyMessage notify) {
|
|||||||
saveImageToFile(this->copiedImage, filepath, filename);
|
saveImageToFile(this->copiedImage, filepath, filename);
|
||||||
spdlog::info("Saved image to {}", filepath.c_str());
|
spdlog::info("Saved image to {}", filepath.c_str());
|
||||||
|
|
||||||
this->signal_close.emit(this->notificationId);
|
this->getSignalClose().emit(this->getNotificationId());
|
||||||
});
|
});
|
||||||
saveToFileButton->set_tooltip_text("Save to File");
|
saveToFileButton->set_tooltip_text("Save to File");
|
||||||
saveToFileButton->add_css_class("notification-button");
|
saveToFileButton->add_css_class("notification-button");
|
||||||
@@ -134,7 +133,7 @@ void CopyNotification::createTextNotification(NotifyMessage notify) {
|
|||||||
auto copyToClipboardButton = Gtk::make_managed<IconButton>(Icon::CONTENT_COPY);
|
auto copyToClipboardButton = Gtk::make_managed<IconButton>(Icon::CONTENT_COPY);
|
||||||
copyToClipboardButton->signal_clicked().connect([this]() {
|
copyToClipboardButton->signal_clicked().connect([this]() {
|
||||||
copyToClipboard(this->copiedText);
|
copyToClipboard(this->copiedText);
|
||||||
this->signal_close.emit(this->notificationId);
|
this->getSignalClose().emit(this->getNotificationId());
|
||||||
});
|
});
|
||||||
copyToClipboardButton->set_tooltip_text("Copy to Clipboard");
|
copyToClipboardButton->set_tooltip_text("Copy to Clipboard");
|
||||||
copyToClipboardButton->add_css_class("notification-icon-button");
|
copyToClipboardButton->add_css_class("notification-icon-button");
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ NotificationWindow::NotificationWindow(uint64_t notificationId, std::shared_ptr<
|
|||||||
if (cb && guard && !*guard) {
|
if (cb && guard && !*guard) {
|
||||||
*guard = true;
|
*guard = true;
|
||||||
cb(action_id);
|
cb(action_id);
|
||||||
this->signal_close.emit(this->notificationId);
|
this->getSignalClose().emit(this->getNotificationId());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
actions_box->append(*btn);
|
actions_box->append(*btn);
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ std::unique_ptr<Gtk::CenterBox> SpotifyNotification::createButtonBox(MprisPlayer
|
|||||||
backButton->signal_clicked().connect([this, mpris]() {
|
backButton->signal_clicked().connect([this, mpris]() {
|
||||||
if (mpris.previous) {
|
if (mpris.previous) {
|
||||||
mpris.previous();
|
mpris.previous();
|
||||||
this->signal_close.emit(this->notificationId);
|
this->getSignalClose().emit(this->getNotificationId());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ std::unique_ptr<Gtk::CenterBox> SpotifyNotification::createButtonBox(MprisPlayer
|
|||||||
nextButton->signal_clicked().connect([this, mpris]() {
|
nextButton->signal_clicked().connect([this, mpris]() {
|
||||||
if (mpris.next) {
|
if (mpris.next) {
|
||||||
mpris.next();
|
mpris.next();
|
||||||
this->signal_close.emit(this->notificationId);
|
this->getSignalClose().emit(this->getNotificationId());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
buttonBox->set_start_widget(*backButton);
|
buttonBox->set_start_widget(*backButton);
|
||||||
|
|||||||
Reference in New Issue
Block a user