cleanup for hyprland lua configs

This commit is contained in:
2026-05-20 06:50:14 +02:00
parent ceca949535
commit 96bf5f3365
5 changed files with 194 additions and 176 deletions
+146 -142
View File
@@ -1,217 +1,221 @@
#include "hyprextras.hpp"
#include "hyprdevices.hpp"
#include <qdir.h>
#include <qjsonarray.h>
#include <qlocalsocket.h>
#include <qloggingcategory.h>
#include <qvariant.h>
Q_LOGGING_CATEGORY(lcHypr, "ZShell.internal.hypr", QtInfoMsg)
namespace ZShell::internal::hypr {
HyprExtras::HyprExtras(QObject* parent)
: QObject(parent)
, m_requestSocket("")
, m_eventSocket("")
, m_socket(nullptr)
, m_socketValid(false)
, 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;
}
: QObject(parent)
, m_requestSocket("")
, m_eventSocket("")
, m_socket(nullptr)
, m_socketValid(false)
, m_devices(new HyprDevices(this)) {
const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE");
if (his.isEmpty()) {
qCWarning(lcHypr) << "$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;
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;
}
}
if (!QDir(hyprDir).exists()) {
qCWarning(lcHypr) << "Hyprland socket directory does not exist. Unable to connect to Hyprland socket.";
return;
}
}
m_requestSocket = hyprDir + "/.socket.sock";
m_eventSocket = hyprDir + "/.socket2.sock";
m_requestSocket = hyprDir + "/.socket.sock";
m_eventSocket = hyprDir + "/.socket2.sock";
refreshOptions();
refreshDevices();
refreshOptions();
refreshDevices();
m_socket = new QLocalSocket(this);
m_socket = new QLocalSocket(this);
QObject::connect(m_socket, &QLocalSocket::errorOccurred, this, &HyprExtras::socketError);
QObject::connect(m_socket, &QLocalSocket::stateChanged, this, &HyprExtras::socketStateChanged);
QObject::connect(m_socket, &QLocalSocket::readyRead, this, &HyprExtras::readEvent);
QObject::connect(m_socket, &QLocalSocket::errorOccurred, this, &HyprExtras::socketError);
QObject::connect(m_socket, &QLocalSocket::stateChanged, this, &HyprExtras::socketStateChanged);
QObject::connect(m_socket, &QLocalSocket::readyRead, this, &HyprExtras::readEvent);
m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly);
m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly);
}
QVariantHash HyprExtras::options() const {
return m_options;
return m_options;
}
HyprDevices* HyprExtras::devices() const {
return m_devices;
return m_devices;
}
void HyprExtras::message(const QString& message) {
if (message.isEmpty()) {
return;
}
if (message.isEmpty()) {
return;
}
makeRequest(message, [](bool success, const QByteArray& res) {
if (!success) {
qWarning() << "HyprExtras::message: request error:" << QString::fromUtf8(res);
}
});
makeRequest(message, [](bool success, const QByteArray& res) {
if (!success) {
qCWarning(lcHypr) << "message: request error:" << QString::fromUtf8(res);
}
});
}
void HyprExtras::batchMessage(const QStringList& messages) {
if (messages.isEmpty()) {
return;
}
if (messages.isEmpty()) {
return;
}
makeRequest("[[BATCH]]" + messages.join(";"), [](bool success, const QByteArray& res) {
if (!success) {
qWarning() << "HyprExtras::batchMessage: request error:" << QString::fromUtf8(res);
}
});
makeRequest("[[BATCH]]" + messages.join(";"), [](bool success, const QByteArray& res) {
if (!success) {
qCWarning(lcHypr) << "batchMessage: request error:" << QString::fromUtf8(res);
}
});
}
void HyprExtras::applyOptions(const QVariantHash& options) {
if (options.isEmpty()) {
return;
}
if (options.isEmpty()) {
return;
}
QString request = "[[BATCH]]";
for (auto it = options.constBegin(); it != options.constEnd(); ++it) {
request += QString("keyword %1 %2;").arg(it.key(), it.value().toString());
}
QString request;
request.reserve(12 + options.size() * 40);
request += QLatin1String("[[BATCH]]");
for (auto it = options.constBegin(); it != options.constEnd(); ++it) {
request += QLatin1String("keyword ") + it.key() + QLatin1Char(' ') + it.value().toString() + QLatin1Char(';');
}
makeRequest(request, [this](bool success, const QByteArray& res) {
if (success) {
refreshOptions();
} else {
qWarning() << "HyprExtras::applyOptions: request error" << QString::fromUtf8(res);
}
});
makeRequest(request, [this](bool success, const QByteArray& res) {
if (success) {
refreshOptions();
} else {
qCWarning(lcHypr) << "applyOptions: request error" << QString::fromUtf8(res);
}
});
}
void HyprExtras::refreshOptions() {
if (!m_optionsRefresh.isNull()) {
m_optionsRefresh->close();
}
if (!m_optionsRefresh.isNull()) {
m_optionsRefresh->close();
}
m_optionsRefresh = makeRequestJson("descriptions", [this](bool success, const QJsonDocument& response) {
m_optionsRefresh.reset();
if (!success) {
return;
}
m_optionsRefresh = makeRequestJson("descriptions", [this](bool success, const QJsonDocument& response) {
m_optionsRefresh.reset();
if (!success) {
return;
}
const auto options = response.array();
bool dirty = false;
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);
}
}
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();
}
});
if (dirty) {
emit optionsChanged();
}
});
}
void HyprExtras::refreshDevices() {
if (!m_devicesRefresh.isNull()) {
m_devicesRefresh->close();
}
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());
}
});
m_devicesRefresh = makeRequestJson("devices", [this](bool success, const QJsonDocument& response) {
m_devicesRefresh.reset();
if (success) {
m_devices->updateLastIpcObject(response.object());
}
});
}
void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const {
if (!m_socketValid) {
qWarning() << "HyprExtras::socketError: unable to connect to Hyprland event socket:" << error;
} else {
qWarning() << "HyprExtras::socketError: Hyprland event socket error:" << error;
}
if (!m_socketValid) {
qCWarning(lcHypr) << "socketError: unable to connect to Hyprland event socket:" << error;
} else {
qCWarning(lcHypr) << "socketError: Hyprland event socket error:" << error;
}
}
void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) {
if (state == QLocalSocket::UnconnectedState && m_socketValid) {
qWarning() << "HyprExtras::socketStateChanged: Hyprland event socket disconnected.";
}
if (state == QLocalSocket::UnconnectedState && m_socketValid) {
qCWarning(lcHypr) << "socketStateChanged: Hyprland event socket disconnected.";
}
m_socketValid = state == QLocalSocket::ConnectedState;
m_socketValid = state == QLocalSocket::ConnectedState;
}
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));
}
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();
}
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));
});
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();
}
const QString& request, const std::function<void(bool, QByteArray)>& callback) {
if (m_requestSocket.isEmpty()) {
return SocketPtr();
}
auto socket = SocketPtr::create(this);
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();
});
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();
});
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();
});
QObject::connect(socket.data(), &QLocalSocket::errorOccurred, this, [=](QLocalSocket::LocalSocketError err) {
qCWarning(lcHypr) << "makeRequest: error making request:" << err << "| request:" << request;
callback(false, {});
socket->close();
});
socket->connectToServer(m_requestSocket);
socket->connectToServer(m_requestSocket);
return socket;
return socket;
}
} // namespace ZShell::internal::hypr
+34 -30
View File
@@ -1,56 +1,60 @@
#pragma once
#include "hyprdevices.hpp"
#include <qlocalsocket.h>
#include <qobject.h>
#include <qqmlintegration.h>
#include <qsharedpointer.h>
#include <qvariant.h>
namespace ZShell::internal::hypr {
class HyprExtras : public QObject {
Q_OBJECT
QML_ELEMENT
class HyprDevices;
Q_PROPERTY(QVariantHash options READ options NOTIFY optionsChanged)
Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
class HyprExtras : public QObject {
Q_OBJECT
QML_ELEMENT
Q_MOC_INCLUDE("hyprdevices.hpp")
Q_PROPERTY(QVariantHash options READ options NOTIFY optionsChanged)
Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
public:
explicit HyprExtras(QObject* parent = nullptr);
explicit HyprExtras(QObject* parent = nullptr);
[[nodiscard]] QVariantHash options() const;
[[nodiscard]] HyprDevices* devices() const;
[[nodiscard]] QVariantHash options() const;
[[nodiscard]] HyprDevices* devices() const;
Q_INVOKABLE void message(const QString& message);
Q_INVOKABLE void batchMessage(const QStringList& messages);
Q_INVOKABLE void applyOptions(const QVariantHash& options);
Q_INVOKABLE void message(const QString& message);
Q_INVOKABLE void batchMessage(const QStringList& messages);
Q_INVOKABLE void applyOptions(const QVariantHash& options);
Q_INVOKABLE void refreshOptions();
Q_INVOKABLE void refreshDevices();
Q_INVOKABLE void refreshOptions();
Q_INVOKABLE void refreshDevices();
signals:
void optionsChanged();
void optionsChanged();
private:
using SocketPtr = QSharedPointer<QLocalSocket>;
using SocketPtr = QSharedPointer<QLocalSocket>;
QString m_requestSocket;
QString m_eventSocket;
QLocalSocket* m_socket;
bool m_socketValid;
QString m_requestSocket;
QString m_eventSocket;
QLocalSocket* m_socket;
bool m_socketValid;
QVariantHash m_options;
HyprDevices* const m_devices;
QVariantHash m_options;
HyprDevices* const m_devices;
SocketPtr m_optionsRefresh;
SocketPtr m_devicesRefresh;
SocketPtr m_optionsRefresh;
SocketPtr m_devicesRefresh;
void socketError(QLocalSocket::LocalSocketError error) const;
void socketStateChanged(QLocalSocket::LocalSocketState state);
void readEvent();
void handleEvent(const QString& event);
void socketError(QLocalSocket::LocalSocketError error) const;
void socketStateChanged(QLocalSocket::LocalSocketState state);
void readEvent();
void handleEvent(const QString& event);
SocketPtr makeRequestJson(const QString& request, const std::function<void(bool, QJsonDocument)>& callback);
SocketPtr makeRequest(const QString& request, const std::function<void(bool, QByteArray)>& callback);
SocketPtr makeRequestJson(const QString& request, const std::function<void(bool, QJsonDocument)>& callback);
SocketPtr makeRequest(const QString& request, const std::function<void(bool, QByteArray)>& callback);
};
} // namespace ZShell::internal::hypr