From ffde4063a0be47f3b0ffae0eecf6ad4f4e7247d2 Mon Sep 17 00:00:00 2001 From: zach Date: Wed, 20 May 2026 07:13:05 +0200 Subject: [PATCH 1/4] experimental hyprland lua support --- Plugins/ZShell/Internal/hyprextras.cpp | 281 +++++++++++++++++-------- 1 file changed, 199 insertions(+), 82 deletions(-) diff --git a/Plugins/ZShell/Internal/hyprextras.cpp b/Plugins/ZShell/Internal/hyprextras.cpp index a869ae2..b5f9845 100644 --- a/Plugins/ZShell/Internal/hyprextras.cpp +++ b/Plugins/ZShell/Internal/hyprextras.cpp @@ -6,11 +6,123 @@ #include #include #include +#include Q_LOGGING_CATEGORY(lcHypr, "ZShell.internal.hypr", QtInfoMsg) namespace ZShell::internal::hypr { +static QString luaEscapeString(const QString& s) { + QString out; + out.reserve(s.size() + 2); + + out += '"'; + for (QChar c : s) { + switch (c.unicode()) { + case '\\': out += "\\\\"; break; + case '"': out += "\\\""; break; + case '\n': out += "\\n"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + default: out += c; break; + } + } + out += '"'; + + return out; +} + +static QString luaValue(const QVariant& v); + +static QString luaArray(const QVariantList& list) { + QStringList parts; + parts.reserve(list.size()); + + for (const auto& item : list) { + parts << luaValue(item); + } + + return "{ " + parts.join(", ") + " }"; +} + +static QString luaMap(const QVariantMap& map) { + QStringList parts; + parts.reserve(map.size()); + + for (auto it = map.cbegin(); it != map.cend(); ++it) { + parts << it.key() + " = " + luaValue(it.value()); + } + + return "{ " + parts.join(", ") + " }"; +} + +static QString luaValue(const QVariant& v) { + if (!v.isValid() || v.isNull()) + return "nil"; + + switch (v.metaType().id()) { + case QMetaType::Bool: + return v.toBool() ? "true" : "false"; + + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::Double: + return v.toString(); + + case QMetaType::QString: + return luaEscapeString(v.toString()); + + case QMetaType::QStringList: + return luaArray(v.toStringList().toList()); + + case QMetaType::QVariantList: + return luaArray(v.toList()); + + case QMetaType::QVariantMap: + return luaMap(v.toMap()); + + case QMetaType::QVariantHash: { + QVariantMap map; + for (auto it = v.toHash().begin(); it != v.toHash().end(); ++it) + map.insert(it.key(), it.value()); + return luaMap(map); + } + + default: + return luaEscapeString(v.toString()); + } +} + +static QString normalizeKey(QString key) { + key = key.trimmed(); + key.replace(':', '.'); + return key; +} + +static QString buildHlConfig(const QString& key, const QVariant& value) { + const QStringList parts = normalizeKey(key).split('.', Qt::SkipEmptyParts); + if (parts.isEmpty()) + return {}; + + QString out = "hl.config({ "; + + for (int i = 0; i < parts.size(); ++i) { + out += parts[i] + " = "; + if (i != parts.size() - 1) + out += "{ "; + } + + out += luaValue(value); + + for (int i = 0; i < parts.size() - 1; ++i) + out += " }"; + + out += " })"; + return out; +} + HyprExtras::HyprExtras(QObject* parent) : QObject(parent) , m_requestSocket("") @@ -18,37 +130,45 @@ HyprExtras::HyprExtras(QObject* parent) , 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."; + qCWarning(lcHypr) << "$HYPRLAND_INSTANCE_SIGNATURE is unset."; return; } - auto hyprDir = QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), 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()) { - qCWarning(lcHypr) << "Hyprland socket directory does not exist. Unable to connect to Hyprland socket."; + qCWarning(lcHypr) << "Hyprland socket directory not found."; return; } } m_requestSocket = hyprDir + "/.socket.sock"; - m_eventSocket = hyprDir + "/.socket2.sock"; + m_eventSocket = hyprDir + "/.socket2.sock"; refreshOptions(); refreshDevices(); 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); + connect(m_socket, &QLocalSocket::errorOccurred, + this, &HyprExtras::socketError); + + connect(m_socket, &QLocalSocket::stateChanged, + this, &HyprExtras::socketStateChanged); + + connect(m_socket, &QLocalSocket::readyRead, + this, &HyprExtras::readEvent); m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly); } + QVariantHash HyprExtras::options() const { return m_options; } @@ -58,148 +178,145 @@ HyprDevices* HyprExtras::devices() const { } void HyprExtras::message(const QString& message) { - if (message.isEmpty()) { - return; - } + if (message.isEmpty()) return; makeRequest(message, [](bool success, const QByteArray& res) { - if (!success) { - qCWarning(lcHypr) << "message: request error:" << QString::fromUtf8(res); - } + if (!success) + qCWarning(lcHypr) << "message failed:" << 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) { - qCWarning(lcHypr) << "batchMessage: request error:" << QString::fromUtf8(res); - } + makeRequest("[[BATCH]]" + messages.join(";"), + [](bool success, const QByteArray& res) { + if (!success) + qCWarning(lcHypr) << "batchMessage failed:" << res; }); } void HyprExtras::applyOptions(const QVariantHash& options) { - if (options.isEmpty()) { + if (options.isEmpty()) return; + + QStringList luaCalls; + luaCalls.reserve(options.size()); + + for (auto it = options.begin(); it != options.end(); ++it) { + const auto call = buildHlConfig(it.key(), it.value()); + if (!call.isEmpty()) + luaCalls << call; } - 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(';'); - } + if (luaCalls.isEmpty()) + return; - makeRequest(request, [this](bool success, const QByteArray& res) { - if (success) { + const QString request = + "eval " + luaCalls.join("; "); + + makeRequest(request, + [this](bool success, const QByteArray& res) { + if (success) refreshOptions(); - } else { - qCWarning(lcHypr) << "applyOptions: request error" << QString::fromUtf8(res); - } + else + qCWarning(lcHypr) << "applyOptions failed:" << res; }); } void HyprExtras::refreshOptions() { - if (!m_optionsRefresh.isNull()) { + if (!m_optionsRefresh.isNull()) m_optionsRefresh->close(); - } - m_optionsRefresh = makeRequestJson("descriptions", [this](bool success, const QJsonDocument& response) { + m_optionsRefresh = makeRequestJson("descriptions", + [this](bool success, const QJsonDocument& response) { m_optionsRefresh.reset(); - if (!success) { - return; - } + if (!success) return; - const auto options = response.array(); + const auto arr = response.array(); bool dirty = false; - for (const auto& o : std::as_const(options)) { + for (const auto& o : arr) { const auto obj = o.toObject(); const auto key = obj.value("value").toString(); - const auto value = obj.value("data").toObject().value("current").toVariant(); + const auto value = + obj.value("data").toObject() + .value("current") + .toVariant(); + if (m_options.value(key) != value) { - dirty = true; m_options.insert(key, value); + dirty = true; } } - if (dirty) { + if (dirty) emit optionsChanged(); - } }); } void HyprExtras::refreshDevices() { - if (!m_devicesRefresh.isNull()) { + if (!m_devicesRefresh.isNull()) m_devicesRefresh->close(); - } - m_devicesRefresh = makeRequestJson("devices", [this](bool success, const QJsonDocument& response) { + m_devicesRefresh = makeRequestJson("devices", + [this](bool success, const QJsonDocument& response) { m_devicesRefresh.reset(); - if (success) { + if (success) m_devices->updateLastIpcObject(response.object()); - } }); } void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const { - if (!m_socketValid) { - qCWarning(lcHypr) << "socketError: unable to connect to Hyprland event socket:" << error; - } else { - qCWarning(lcHypr) << "socketError: Hyprland event socket error:" << error; - } + qCWarning(lcHypr) << "socket error:" << error; } void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) { - 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(">>")); + auto line = m_socket->readLine(); + if (line.isEmpty()) break; + + line.chop(1); + const auto event = line.left(line.indexOf(">>")); + handleEvent(QString::fromUtf8(event)); } } void HyprExtras::handleEvent(const QString& event) { - if (event == "configreloaded") { + if (event == "configreloaded") refreshOptions(); - } else if (event == "activelayout") { + else if (event == "activelayout") refreshDevices(); - } } HyprExtras::SocketPtr HyprExtras::makeRequestJson( - const QString& request, const std::function& callback) { - return makeRequest("j/" + request, [callback](bool success, const QByteArray& response) { - callback(success, QJsonDocument::fromJson(response)); + const QString& request, + const std::function& callback) { + + return makeRequest("j/" + request, + [callback](bool success, const QByteArray& res) { + callback(success, QJsonDocument::fromJson(res)); }); } HyprExtras::SocketPtr HyprExtras::makeRequest( - const QString& request, const std::function& callback) { - if (m_requestSocket.isEmpty()) { + const QString& request, + const std::function& 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)); + connect(socket.data(), &QLocalSocket::connected, this, [=, this]() { + connect(socket.data(), &QLocalSocket::readyRead, + this, [socket, callback]() { + callback(true, socket->readAll()); socket->close(); }); @@ -207,14 +324,14 @@ HyprExtras::SocketPtr HyprExtras::makeRequest( socket->flush(); }); - QObject::connect(socket.data(), &QLocalSocket::errorOccurred, this, [=](QLocalSocket::LocalSocketError err) { - qCWarning(lcHypr) << "makeRequest: error making request:" << err << "| request:" << request; + connect(socket.data(), &QLocalSocket::errorOccurred, + this, [=](QLocalSocket::LocalSocketError err) { + qCWarning(lcHypr) << "request error:" << err << request; callback(false, {}); socket->close(); }); socket->connectToServer(m_requestSocket); - return socket; } From b8524ff621ae7e16c2685ebf56f55207febcab5e Mon Sep 17 00:00:00 2001 From: zach Date: Wed, 20 May 2026 07:17:38 +0200 Subject: [PATCH 2/4] experimental hyprland lua support --- Plugins/ZShell/Internal/hyprextras.cpp | 290 ++++++++++++++----------- 1 file changed, 168 insertions(+), 122 deletions(-) diff --git a/Plugins/ZShell/Internal/hyprextras.cpp b/Plugins/ZShell/Internal/hyprextras.cpp index b5f9845..0d372ce 100644 --- a/Plugins/ZShell/Internal/hyprextras.cpp +++ b/Plugins/ZShell/Internal/hyprextras.cpp @@ -5,30 +5,45 @@ #include #include #include -#include #include +#include +#include Q_LOGGING_CATEGORY(lcHypr, "ZShell.internal.hypr", QtInfoMsg) namespace ZShell::internal::hypr { +namespace { + static QString luaEscapeString(const QString& s) { QString out; out.reserve(s.size() + 2); + out += QLatin1Char('"'); - out += '"'; - for (QChar c : s) { + for (const QChar c : s) { switch (c.unicode()) { - case '\\': out += "\\\\"; break; - case '"': out += "\\\""; break; - case '\n': out += "\\n"; break; - case '\r': out += "\\r"; break; - case '\t': out += "\\t"; break; - default: out += c; break; + case '\\': + out += QLatin1String(R"(\\)"); + break; + case '"': + out += QLatin1String(R"(\")"); + break; + case '\n': + out += QLatin1String(R"(\n)"); + break; + case '\r': + out += QLatin1String(R"(\r)"); + break; + case '\t': + out += QLatin1String(R"(\t)"); + break; + default: + out += c; + break; } } - out += '"'; + out += QLatin1Char('"'); return out; } @@ -42,7 +57,29 @@ static QString luaArray(const QVariantList& list) { parts << luaValue(item); } - return "{ " + parts.join(", ") + " }"; + return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }"); +} + +static QString luaArray(const QStringList& list) { + QStringList parts; + parts.reserve(list.size()); + + for (const auto& item : list) { + parts << luaEscapeString(item); + } + + return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }"); +} + +static QString luaMapFromHash(const QVariantHash& hash) { + QStringList parts; + parts.reserve(hash.size()); + + for (auto it = hash.cbegin(); it != hash.cend(); ++it) { + parts << luaEscapeString(it.key()) + QLatin1String(" = ") + luaValue(it.value()); + } + + return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }"); } static QString luaMap(const QVariantMap& map) { @@ -50,19 +87,20 @@ static QString luaMap(const QVariantMap& map) { parts.reserve(map.size()); for (auto it = map.cbegin(); it != map.cend(); ++it) { - parts << it.key() + " = " + luaValue(it.value()); + parts << luaEscapeString(it.key()) + QLatin1String(" = ") + luaValue(it.value()); } - return "{ " + parts.join(", ") + " }"; + return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }"); } static QString luaValue(const QVariant& v) { - if (!v.isValid() || v.isNull()) - return "nil"; + if (!v.isValid() || v.isNull()) { + return QLatin1String("nil"); + } switch (v.metaType().id()) { case QMetaType::Bool: - return v.toBool() ? "true" : "false"; + return v.toBool() ? QLatin1String("true") : QLatin1String("false"); case QMetaType::Int: case QMetaType::UInt: @@ -75,7 +113,7 @@ static QString luaValue(const QVariant& v) { return luaEscapeString(v.toString()); case QMetaType::QStringList: - return luaArray(v.toStringList().toList()); + return luaArray(v.toStringList()); case QMetaType::QVariantList: return luaArray(v.toList()); @@ -83,46 +121,50 @@ static QString luaValue(const QVariant& v) { case QMetaType::QVariantMap: return luaMap(v.toMap()); - case QMetaType::QVariantHash: { - QVariantMap map; - for (auto it = v.toHash().begin(); it != v.toHash().end(); ++it) - map.insert(it.key(), it.value()); - return luaMap(map); - } + case QMetaType::QVariantHash: + return luaMapFromHash(v.toHash()); default: return luaEscapeString(v.toString()); } } -static QString normalizeKey(QString key) { +static QString normalizeOptionPath(QString key) { key = key.trimmed(); - key.replace(':', '.'); + key.replace(QLatin1Char(':'), QLatin1Char('.')); return key; } -static QString buildHlConfig(const QString& key, const QVariant& value) { - const QStringList parts = normalizeKey(key).split('.', Qt::SkipEmptyParts); - if (parts.isEmpty()) +static QString buildHlConfigCall(const QString& key, const QVariant& value) { + const auto parts = normalizeOptionPath(key).split(QLatin1Char('.'), Qt::SkipEmptyParts); + if (parts.isEmpty()) { return {}; + } - QString out = "hl.config({ "; + QString out; + out.reserve(32 + key.size() + value.toString().size()); + out += QLatin1String("hl.config({ "); for (int i = 0; i < parts.size(); ++i) { - out += parts[i] + " = "; - if (i != parts.size() - 1) - out += "{ "; + out += parts.at(i); + out += QLatin1String(" = "); + if (i + 1 < parts.size()) { + out += QLatin1String("{ "); + } } out += luaValue(value); - for (int i = 0; i < parts.size() - 1; ++i) - out += " }"; + for (int i = 0; i + 1 < parts.size(); ++i) { + out += QLatin1String(" }"); + } - out += " })"; + out += QLatin1String(" })"); return out; } +} // namespace + HyprExtras::HyprExtras(QObject* parent) : QObject(parent) , m_requestSocket("") @@ -130,45 +172,37 @@ HyprExtras::HyprExtras(QObject* parent) , 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."; + 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); - + auto hyprDir = QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his); if (!QDir(hyprDir).exists()) { - hyprDir = "/tmp/hypr/" + his; + hyprDir = QStringLiteral("/tmp/hypr/") + his; + if (!QDir(hyprDir).exists()) { - qCWarning(lcHypr) << "Hyprland socket directory not found."; + 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 + QStringLiteral("/.socket.sock"); + m_eventSocket = hyprDir + QStringLiteral("/.socket2.sock"); refreshOptions(); refreshDevices(); m_socket = new QLocalSocket(this); - connect(m_socket, &QLocalSocket::errorOccurred, - this, &HyprExtras::socketError); - - connect(m_socket, &QLocalSocket::stateChanged, - this, &HyprExtras::socketStateChanged); - - 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); } - QVariantHash HyprExtras::options() const { return m_options; } @@ -178,145 +212,157 @@ HyprDevices* HyprExtras::devices() const { } void HyprExtras::message(const QString& message) { - if (message.isEmpty()) return; + if (message.isEmpty()) { + return; + } makeRequest(message, [](bool success, const QByteArray& res) { - if (!success) - qCWarning(lcHypr) << "message failed:" << 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(";"), + makeRequest(QStringLiteral("[[BATCH]]") + messages.join(QLatin1Char(';')), [](bool success, const QByteArray& res) { - if (!success) - qCWarning(lcHypr) << "batchMessage failed:" << res; + if (!success) { + qCWarning(lcHypr) << "batchMessage: request error:" << QString::fromUtf8(res); + } }); } void HyprExtras::applyOptions(const QVariantHash& options) { - if (options.isEmpty()) + if (options.isEmpty()) { return; - - QStringList luaCalls; - luaCalls.reserve(options.size()); - - for (auto it = options.begin(); it != options.end(); ++it) { - const auto call = buildHlConfig(it.key(), it.value()); - if (!call.isEmpty()) - luaCalls << call; } - if (luaCalls.isEmpty()) + QStringList calls; + calls.reserve(options.size()); + + for (auto it = options.constBegin(); it != options.constEnd(); ++it) { + const auto call = buildHlConfigCall(it.key(), it.value()); + if (!call.isEmpty()) { + calls << call; + } + } + + if (calls.isEmpty()) { return; + } - const QString request = - "eval " + luaCalls.join("; "); - - makeRequest(request, - [this](bool success, const QByteArray& res) { - if (success) + makeRequest(QStringLiteral("eval ") + calls.join(QLatin1String("; ")), [this](bool success, const QByteArray& res) { + if (success) { refreshOptions(); - else - qCWarning(lcHypr) << "applyOptions failed:" << res; + } else { + qCWarning(lcHypr) << "applyOptions: request error" << QString::fromUtf8(res); + } }); } void HyprExtras::refreshOptions() { - if (!m_optionsRefresh.isNull()) + if (!m_optionsRefresh.isNull()) { m_optionsRefresh->close(); + } - m_optionsRefresh = makeRequestJson("descriptions", - [this](bool success, const QJsonDocument& response) { + m_optionsRefresh = makeRequestJson(QStringLiteral("descriptions"), [this](bool success, const QJsonDocument& response) { m_optionsRefresh.reset(); - if (!success) return; + if (!success) { + return; + } - const auto arr = response.array(); + const auto options = response.array(); bool dirty = false; - for (const auto& o : arr) { + 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(); + const auto key = obj.value(QStringLiteral("value")).toString(); + const auto value = obj.value(QStringLiteral("data")).toObject().value(QStringLiteral("current")).toVariant(); if (m_options.value(key) != value) { - m_options.insert(key, value); dirty = true; + m_options.insert(key, value); } } - if (dirty) + if (dirty) { emit optionsChanged(); + } }); } void HyprExtras::refreshDevices() { - if (!m_devicesRefresh.isNull()) + if (!m_devicesRefresh.isNull()) { m_devicesRefresh->close(); + } - m_devicesRefresh = makeRequestJson("devices", - [this](bool success, const QJsonDocument& response) { + m_devicesRefresh = makeRequestJson(QStringLiteral("devices"), [this](bool success, const QJsonDocument& response) { m_devicesRefresh.reset(); - if (success) + if (success) { m_devices->updateLastIpcObject(response.object()); + } }); } void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const { - qCWarning(lcHypr) << "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) { - m_socketValid = (state == QLocalSocket::ConnectedState); + if (state == QLocalSocket::UnconnectedState && m_socketValid) { + qCWarning(lcHypr) << "socketStateChanged: Hyprland event socket disconnected."; + } + + m_socketValid = state == QLocalSocket::ConnectedState; } void HyprExtras::readEvent() { while (true) { - auto line = m_socket->readLine(); - if (line.isEmpty()) break; - - line.chop(1); - const auto event = line.left(line.indexOf(">>")); - + auto rawEvent = m_socket->readLine(); + if (rawEvent.isEmpty()) { + break; + } + rawEvent.truncate(rawEvent.length() - 1); + const auto event = QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>")); handleEvent(QString::fromUtf8(event)); } } void HyprExtras::handleEvent(const QString& event) { - if (event == "configreloaded") + if (event == QStringLiteral("configreloaded")) { refreshOptions(); - else if (event == "activelayout") + } else if (event == QStringLiteral("activelayout")) { refreshDevices(); + } } HyprExtras::SocketPtr HyprExtras::makeRequestJson( - const QString& request, - const std::function& callback) { - - return makeRequest("j/" + request, - [callback](bool success, const QByteArray& res) { - callback(success, QJsonDocument::fromJson(res)); + const QString& request, const std::function& callback) { + return makeRequest(QStringLiteral("j/") + request, [callback](bool success, const QByteArray& response) { + callback(success, QJsonDocument::fromJson(response)); }); } HyprExtras::SocketPtr HyprExtras::makeRequest( - const QString& request, - const std::function& callback) { - - if (m_requestSocket.isEmpty()) + const QString& request, const std::function& callback) { + if (m_requestSocket.isEmpty()) { return SocketPtr(); + } auto socket = SocketPtr::create(this); - connect(socket.data(), &QLocalSocket::connected, this, [=, this]() { - connect(socket.data(), &QLocalSocket::readyRead, - this, [socket, callback]() { - callback(true, socket->readAll()); + 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(); }); @@ -324,14 +370,14 @@ HyprExtras::SocketPtr HyprExtras::makeRequest( socket->flush(); }); - connect(socket.data(), &QLocalSocket::errorOccurred, - this, [=](QLocalSocket::LocalSocketError err) { - qCWarning(lcHypr) << "request error:" << err << request; + 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); + return socket; } From b8af60008d4cba5e787cb8ad25faa1c56eb67782 Mon Sep 17 00:00:00 2001 From: zach Date: Wed, 20 May 2026 07:30:38 +0200 Subject: [PATCH 3/4] fixed applying hyprland options and rules, as well as fetching --- Config/DynamicColors.qml | 30 ++++++++++++++++++++++++++---- Helpers/GameMode.qml | 10 +++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/Config/DynamicColors.qml b/Config/DynamicColors.qml index 876c7d7..45c6902 100644 --- a/Config/DynamicColors.qml +++ b/Config/DynamicColors.qml @@ -80,10 +80,32 @@ Singleton { } function reloadHyprRules(): void { - const barStr = Hyprland.usingLua ? `eval 'hl.layer_rule({ match = { namespace = "ZShell-Bar" }, %1 = true, %2 = true })'` : "keyword layerrule %1 %2, match:namespace ZShell-Bar"; - const authStr = Hyprland.usingLua ? `eval 'hl.layer_rule({ match = { namespace = "ZShell-Auth" }, %1 = true, %2 = true })'` : "keyword layerrule %1 %2, match:namespace ZShell-Auth"; - Hypr.extras.batchMessage([barStr.arg("blur").arg(transparency.enabled ? 1 : 0), barStr.arg("ignore_alpha").arg(transparency.base - 0.03)]); - Hypr.extras.batchMessage([authStr.arg("blur").arg(transparency.enabled ? 1 : 0), authStr.arg("ignore_alpha").arg(transparency.base - 0.03)]); + const blur = transparency.enabled ? 1 : 0; + const alpha = transparency.base - 0.03; + + const rules = ` + hl.layer_rule({ + match = { namespace = "ZShell-Bar" }, + blur = ${blur} + }) + + hl.layer_rule({ + match = { namespace = "ZShell-Bar" }, + ignore_alpha = ${alpha} + }) + + hl.layer_rule({ + match = { namespace = "ZShell-Auth" }, + blur = ${blur} + }) + + hl.layer_rule({ + match = { namespace = "ZShell-Auth" }, + ignore_alpha = ${alpha} + }) + `; + + Hypr.extras.message(`eval ${rules}`); } function setMode(mode: string): void { diff --git a/Helpers/GameMode.qml b/Helpers/GameMode.qml index 7bc4d32..6cbeb68 100644 --- a/Helpers/GameMode.qml +++ b/Helpers/GameMode.qml @@ -13,11 +13,11 @@ Singleton { function setHyprConf(): void { Hypr.extras.applyOptions({ - "animations:enabled": 0, - "decoration:shadow:enabled": 0, - "decoration:blur:enabled": 0, - "general:border_size": 0, - "decoration:rounding": 0 + "animations.enabled": 0, + "decoration.shadow.enabled": 0, + "decoration.blur.enabled": 0, + "general.border_size": 0, + "decoration.rounding": 0 }); } From 853b6839624a818bb828bac7893e2b63c7047ddd Mon Sep 17 00:00:00 2001 From: zach Date: Wed, 20 May 2026 07:44:56 +0200 Subject: [PATCH 4/4] Changed base deform numbers, less bouncy --- Drawers/Windows.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Drawers/Windows.qml b/Drawers/Windows.qml index 9f9e4ca..37bd73c 100644 --- a/Drawers/Windows.qml +++ b/Drawers/Windows.qml @@ -182,7 +182,7 @@ Variants { property real extraHeight: 0.2 - deformAmount: 0.08 + deformAmount: 0.06 implicitHeight: panels.dashboard.height * (1 + extraHeight) implicitWidth: panels.dashboard.width panel: panels.dashboardWrapper @@ -196,7 +196,7 @@ Variants { property real extraHeight: 0.2 - deformAmount: 0.08 + deformAmount: 0.06 implicitHeight: panels.launcher.height * (1 + extraHeight) panel: panels.launcher radius: Appearance.rounding.smallest + 5 @@ -207,7 +207,7 @@ Variants { id: sidebarBg bottomLeftRadius: 0 - deformAmount: 0.08 + deformAmount: 0.04 exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg] implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2 panel: panels.sidebar @@ -262,7 +262,7 @@ Variants { PanelBg { id: resourcesBg - deformAmount: 0.08 + deformAmount: 0.05 implicitHeight: panels.resources.height implicitWidth: panels.resources.width panel: panels.resourcesWrapper @@ -276,10 +276,10 @@ Variants { property real extraHeight: 0.2 - deformAmount: 0.08 + deformAmount: 0.03 implicitHeight: panels.settings.height * (1 + extraHeight) implicitWidth: panels.settings.width - panel: panels.settingsWrapper + panel: panels.settings radius: Appearance.rounding.large topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller topRightRadius: Appearance.rounding.large + Appearance.padding.smaller