diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-09-24 23:48:47 +1000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-24 23:48:47 +1000 |
| commit | 57e3989185b8209a11d79bac477479019d515180 (patch) | |
| tree | 27e94dcaef9f41bb8f294eab1a91a27ce4da03d0 /plugin/src/Caelestia/Internal/hyprextras.cpp | |
| parent | plugin: sub namespace modules (diff) | |
| download | caelestia-shell-57e3989185b8209a11d79bac477479019d515180.tar.gz caelestia-shell-57e3989185b8209a11d79bac477479019d515180.tar.bz2 caelestia-shell-57e3989185b8209a11d79bac477479019d515180.zip | |
plugin: add hypr extras (#690)
* move machine
* works
* prevent duplicate refreshes
* use instead of hyprctl proc
Diffstat (limited to 'plugin/src/Caelestia/Internal/hyprextras.cpp')
| -rw-r--r-- | plugin/src/Caelestia/Internal/hyprextras.cpp | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/plugin/src/Caelestia/Internal/hyprextras.cpp b/plugin/src/Caelestia/Internal/hyprextras.cpp new file mode 100644 index 0000000..23495b2 --- /dev/null +++ b/plugin/src/Caelestia/Internal/hyprextras.cpp @@ -0,0 +1,158 @@ +#include "hyprextras.hpp" + +#include <qdir.h> +#include <qjsonarray.h> +#include <qlocalsocket.h> +#include <qvariant.h> + +namespace caelestia::internal::hypr { + +HyprExtras::HyprExtras(QObject* parent) + : QObject(parent) + , m_requestSocket("") + , m_eventSocket("") + , m_socket(nullptr) + , m_devices(new HyprDevices(this)) { + const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE"); + if (his.isEmpty()) { + qWarning() + << "HyprExtras::HyprExtras: $HYPRLAND_INSTANCE_SIGNATURE is unset. Unable to connect to Hyprland socket."; + return; + } + + auto hyprDir = QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his); + if (!QDir(hyprDir).exists()) { + hyprDir = "/tmp/hypr/" + his; + + if (!QDir(hyprDir).exists()) { + qWarning() << "HyprExtras::HyprExtras: Hyprland socket directory does not exist. Unable to connect to " + "Hyprland socket."; + return; + } + } + + m_requestSocket = hyprDir + "/.socket.sock"; + m_eventSocket = hyprDir + "/.event.sock"; + + refreshOptions(); + refreshDevices(); + + m_socket = new QLocalSocket(this); + QObject::connect(m_socket, &QLocalSocket::readyRead, this, &HyprExtras::readEvent); + m_socket->connectToServer(m_eventSocket); +} + +QVariantHash HyprExtras::options() const { + return m_options; +} + +HyprDevices* HyprExtras::devices() const { + return m_devices; +} + +void HyprExtras::message(const QString& message) { + makeRequest(message, [](bool, const QByteArray&) { + }); +} + +void HyprExtras::refreshOptions() { + if (!m_optionsRefresh.isNull()) { + m_optionsRefresh->close(); + } + + m_optionsRefresh = makeRequestJson("descriptions", [this](bool success, const QJsonDocument& response) { + m_optionsRefresh.reset(); + if (!success) { + return; + } + + const auto options = response.array(); + bool dirty = false; + + for (const auto& o : std::as_const(options)) { + const auto obj = o.toObject(); + const auto key = obj.value("value").toString(); + const auto value = obj.value("data").toObject().value("current").toVariant(); + if (m_options.value(key) != value) { + dirty = true; + m_options.insert(key, value); + } + } + + if (dirty) { + emit optionsChanged(); + } + }); +} + +void HyprExtras::refreshDevices() { + if (!m_devicesRefresh.isNull()) { + m_devicesRefresh->close(); + } + + m_devicesRefresh = makeRequestJson("devices", [this](bool success, const QJsonDocument& response) { + m_devicesRefresh.reset(); + if (success) { + m_devices->updateLastIpcObject(response.object()); + } + }); +} + +void HyprExtras::readEvent() { + while (true) { + auto rawEvent = m_socket->readLine(); + if (rawEvent.isEmpty()) { + break; + } + rawEvent.truncate(rawEvent.length() - 1); // Remove trailing \n + const auto event = QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>")); + handleEvent(QString::fromUtf8(event)); + } +} + +void HyprExtras::handleEvent(const QString& event) { + if (event == "configreloaded") { + refreshOptions(); + } else if (event == "activelayout") { + refreshDevices(); + } +} + +HyprExtras::SocketPtr HyprExtras::makeRequestJson( + const QString& request, const std::function<void(bool, QJsonDocument)>& callback) { + return makeRequest("j/" + request, [callback](bool success, const QByteArray& response) { + callback(success, QJsonDocument::fromJson(response)); + }); +} + +HyprExtras::SocketPtr HyprExtras::makeRequest( + const QString& request, const std::function<void(bool, QByteArray)>& callback) { + if (m_requestSocket.isEmpty()) { + return SocketPtr(); + } + + auto socket = SocketPtr::create(this); + + QObject::connect(socket.data(), &QLocalSocket::connected, this, [=, this]() { + QObject::connect(socket.data(), &QLocalSocket::readyRead, this, [socket, callback]() { + const auto response = socket->readAll(); + callback(true, std::move(response)); + socket->close(); + }); + + socket->write(request.toUtf8()); + socket->flush(); + }); + + QObject::connect(socket.data(), &QLocalSocket::errorOccurred, this, [=](QLocalSocket::LocalSocketError err) { + qWarning() << "HyprExtras::makeRequest: error making request:" << err << "| request:" << request; + callback(false, {}); + socket->close(); + }); + + socket->connectToServer(m_requestSocket); + + return socket; +} + +} // namespace caelestia::internal::hypr |