summaryrefslogtreecommitdiff
path: root/plugin/src/Caelestia/Internal/hyprextras.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/src/Caelestia/Internal/hyprextras.cpp')
-rw-r--r--plugin/src/Caelestia/Internal/hyprextras.cpp158
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