mirror of
https://github.com/quickshell-mirror/quickshell.git
synced 2026-10-27 02:54:36 +00:00
hyprland/ipc: support workspace addresses
This commit is contained in:
7 files changed
+86
-48
No files matched your search
@@ -1,3 +1,7 @@
|
||||
## Other Changes
|
||||
|
||||
- Added support for Hyprland's new workspace address format.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- Fixed main process crashes on pam subprocess misbehavior.
|
||||
|
||||
@@ -188,12 +188,12 @@ void HyprlandIpc::makeRequest(
|
||||
};
|
||||
|
||||
auto errorCallback =
|
||||
[this, requestSocket, request, callback](QLocalSocket::LocalSocketError error) {
|
||||
qCWarning(logHyprlandIpc) << "Error making request:" << error << "request:" << request;
|
||||
QObject::disconnect(requestSocket, nullptr, this, nullptr);
|
||||
requestSocket->deleteLater();
|
||||
callback(false, {});
|
||||
};
|
||||
[this, requestSocket, request, callback](QLocalSocket::LocalSocketError error) {
|
||||
qCWarning(logHyprlandIpc) << "Error making request:" << error << "request:" << request;
|
||||
QObject::disconnect(requestSocket, nullptr, this, nullptr);
|
||||
requestSocket->deleteLater();
|
||||
callback(false, {});
|
||||
};
|
||||
|
||||
QObject::connect(requestSocket, &QLocalSocket::connected, this, connectedCallback);
|
||||
QObject::connect(requestSocket, &QLocalSocket::errorOccurred, this, errorCallback);
|
||||
@@ -307,7 +307,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
const auto& mList = this->mMonitors.valueList();
|
||||
auto name = QString::fromUtf8(event->data);
|
||||
|
||||
auto monitorIter = std::ranges::find_if(mList, [name](HyprlandMonitor* m) {
|
||||
auto monitorIter = std::ranges::find_if(mList, [&](HyprlandMonitor* m) {
|
||||
return m->bindableName().value() == name;
|
||||
});
|
||||
|
||||
@@ -332,10 +332,10 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
} else if (event->name == "createworkspacev2") {
|
||||
auto args = event->parseView(2);
|
||||
|
||||
auto id = args.at(0).toInt();
|
||||
auto address = QString::fromUtf8(args.at(0));
|
||||
auto name = QString::fromUtf8(args.at(1));
|
||||
|
||||
qCDebug(logHyprlandIpc) << "Workspace created with id" << id << "name" << name;
|
||||
qCDebug(logHyprlandIpc) << "Workspace created with address" << address << "name" << name;
|
||||
|
||||
auto* workspace = this->findWorkspaceByName(name, false);
|
||||
auto existed = workspace != nullptr;
|
||||
@@ -344,7 +344,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
workspace = new HyprlandWorkspace(this);
|
||||
}
|
||||
|
||||
workspace->updateInitial(id, name);
|
||||
workspace->updateInitial(address, name);
|
||||
|
||||
if (!existed) {
|
||||
this->refreshWorkspaces(false);
|
||||
@@ -353,17 +353,17 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
} else if (event->name == "destroyworkspacev2") {
|
||||
auto args = event->parseView(2);
|
||||
|
||||
auto id = args.at(0).toInt();
|
||||
auto address = QString::fromUtf8(args.at(0));
|
||||
auto name = QString::fromUtf8(args.at(1));
|
||||
|
||||
const auto& mList = this->mWorkspaces.valueList();
|
||||
|
||||
auto workspaceIter = std::ranges::find_if(mList, [id](HyprlandWorkspace* m) {
|
||||
return m->bindableId().value() == id;
|
||||
auto workspaceIter = std::ranges::find_if(mList, [&](HyprlandWorkspace* m) {
|
||||
return m->bindableAddress().value() == address;
|
||||
});
|
||||
|
||||
if (workspaceIter == mList.end()) {
|
||||
qCWarning(logHyprlandIpc) << "Got removal for workspace id" << id << "name" << name
|
||||
qCWarning(logHyprlandIpc) << "Got removal for workspace id" << address << "name" << name
|
||||
<< "which was not previously tracked.";
|
||||
return;
|
||||
}
|
||||
@@ -371,7 +371,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
auto index = workspaceIter - mList.begin();
|
||||
auto* workspace = *workspaceIter;
|
||||
|
||||
qCDebug(logHyprlandIpc) << "Workspace removed with id" << id << "name" << name;
|
||||
qCDebug(logHyprlandIpc) << "Workspace removed with id" << address << "name" << name;
|
||||
this->mWorkspaces.removeAt(index);
|
||||
|
||||
// workspaces have not been observed to be referenced after deletion
|
||||
@@ -400,10 +400,10 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
this->setFocusedMonitor(monitor);
|
||||
monitor->setActiveWorkspace(workspace);
|
||||
qCDebug(logHyprlandIpc) << "Monitor" << name << "focused with workspace"
|
||||
<< (workspace ? workspace->bindableId().value() : -1);
|
||||
<< (workspace ? workspace->bindableAddress().value() : "<unk>");
|
||||
} else if (event->name == "workspacev2") {
|
||||
auto args = event->parseView(2);
|
||||
auto id = args.at(0).toInt();
|
||||
auto id = QString::fromUtf8(args.at(0));
|
||||
auto name = QString::fromUtf8(args.at(1));
|
||||
|
||||
if (this->bFocusedMonitor != nullptr) {
|
||||
@@ -414,7 +414,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
}
|
||||
} else if (event->name == "moveworkspacev2") {
|
||||
auto args = event->parseView(3);
|
||||
auto id = args.at(0).toInt();
|
||||
auto id = QString::fromUtf8(args.at(0));
|
||||
auto name = QString::fromUtf8(args.at(1));
|
||||
auto monitorName = QString::fromUtf8(args.at(2));
|
||||
|
||||
@@ -425,18 +425,18 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
workspace->setMonitor(monitor);
|
||||
} else if (event->name == "renameworkspace") {
|
||||
auto args = event->parseView(2);
|
||||
auto id = args.at(0).toInt();
|
||||
auto address = QString::fromUtf8(args.at(0));
|
||||
auto name = QString::fromUtf8(args.at(1));
|
||||
|
||||
const auto& mList = this->mWorkspaces.valueList();
|
||||
|
||||
auto workspaceIter = std::ranges::find_if(mList, [id](HyprlandWorkspace* m) {
|
||||
return m->bindableId().value() == id;
|
||||
auto workspaceIter = std::ranges::find_if(mList, [&](HyprlandWorkspace* m) {
|
||||
return m->bindableAddress().value() == address;
|
||||
});
|
||||
|
||||
if (workspaceIter == mList.end()) return;
|
||||
|
||||
qCDebug(logHyprlandIpc) << "Workspace with id" << id << "renamed from"
|
||||
qCDebug(logHyprlandIpc) << "Workspace with address" << address << "renamed from"
|
||||
<< (*workspaceIter)->bindableName().value() << "to" << name;
|
||||
|
||||
(*workspaceIter)->bindableName().setValue(name);
|
||||
@@ -488,7 +488,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
if (!ok) return;
|
||||
|
||||
const auto& mList = this->mToplevels.valueList();
|
||||
auto toplevelIter = std::ranges::find_if(mList, [windowAddress](HyprlandToplevel* m) {
|
||||
auto toplevelIter = std::ranges::find_if(mList, [&](HyprlandToplevel* m) {
|
||||
return m->address() == windowAddress;
|
||||
});
|
||||
|
||||
@@ -580,14 +580,17 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
}
|
||||
}
|
||||
|
||||
HyprlandWorkspace*
|
||||
HyprlandIpc::findWorkspaceByName(const QString& name, bool createIfMissing, qint32 id) {
|
||||
HyprlandWorkspace* HyprlandIpc::findWorkspaceByName(
|
||||
const QString& name,
|
||||
bool createIfMissing,
|
||||
const QString& address
|
||||
) {
|
||||
const auto& mList = this->mWorkspaces.valueList();
|
||||
HyprlandWorkspace* workspace = nullptr;
|
||||
|
||||
if (id != -1) {
|
||||
if (!address.isEmpty()) {
|
||||
auto workspaceIter = std::ranges::find_if(mList, [&](HyprlandWorkspace* m) {
|
||||
return m->bindableId().value() == id;
|
||||
return m->bindableAddress().value() == address;
|
||||
});
|
||||
|
||||
workspace = workspaceIter == mList.end() ? nullptr : *workspaceIter;
|
||||
@@ -605,10 +608,11 @@ HyprlandIpc::findWorkspaceByName(const QString& name, bool createIfMissing, qint
|
||||
return workspace;
|
||||
} else if (createIfMissing) {
|
||||
qCDebug(logHyprlandIpc) << "Workspace" << name
|
||||
<< "requested before creation, performing early init with id" << id;
|
||||
<< "requested before creation, performing early init with address"
|
||||
<< address;
|
||||
|
||||
auto* workspace = new HyprlandWorkspace(this);
|
||||
workspace->updateInitial(id, name);
|
||||
workspace->updateInitial(address, name);
|
||||
this->mWorkspaces.insertObjectSorted(workspace, &HyprlandIpc::compareWorkspaces);
|
||||
return workspace;
|
||||
} else {
|
||||
@@ -628,15 +632,16 @@ void HyprlandIpc::refreshWorkspaces(bool canCreate) {
|
||||
auto json = QJsonDocument::fromJson(resp).array();
|
||||
|
||||
const auto& mList = this->mWorkspaces.valueList();
|
||||
auto ids = QVector<quint32>();
|
||||
auto addresses = QVector<QString>();
|
||||
|
||||
for (auto entry: json) {
|
||||
auto object = entry.toObject().toVariantMap();
|
||||
|
||||
auto id = object.value("id").toInt();
|
||||
auto address = object.value("address").toString();
|
||||
if (address.isEmpty()) address = QString::number(object.value("id").toInt());
|
||||
|
||||
auto workspaceIter = std::ranges::find_if(mList, [&](HyprlandWorkspace* m) {
|
||||
return m->bindableId().value() == id;
|
||||
return m->bindableAddress().value() == address;
|
||||
});
|
||||
|
||||
// Only fall back to name-based filtering as a last resort, for workspaces where
|
||||
@@ -645,7 +650,7 @@ void HyprlandIpc::refreshWorkspaces(bool canCreate) {
|
||||
auto name = object.value("name").toString();
|
||||
|
||||
workspaceIter = std::ranges::find_if(mList, [&](HyprlandWorkspace* m) {
|
||||
return m->bindableId().value() == -1 && m->bindableName().value() == name;
|
||||
return m->bindableAddress().value().isEmpty() && m->bindableName().value() == name;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -663,14 +668,14 @@ void HyprlandIpc::refreshWorkspaces(bool canCreate) {
|
||||
this->mWorkspaces.insertObjectSorted(workspace, &HyprlandIpc::compareWorkspaces);
|
||||
}
|
||||
|
||||
ids.push_back(id);
|
||||
addresses.push_back(address);
|
||||
}
|
||||
|
||||
if (canCreate) {
|
||||
auto removedWorkspaces = QVector<HyprlandWorkspace*>();
|
||||
|
||||
for (auto* workspace: mList) {
|
||||
if (!ids.contains(workspace->bindableId().value())) {
|
||||
if (!addresses.contains(workspace->bindableAddress().value())) {
|
||||
removedWorkspaces.push_back(workspace);
|
||||
}
|
||||
}
|
||||
@@ -752,7 +757,7 @@ HyprlandMonitor*
|
||||
HyprlandIpc::findMonitorByName(const QString& name, bool createIfMissing, qint32 id) {
|
||||
const auto& mList = this->mMonitors.valueList();
|
||||
|
||||
auto monitorIter = std::ranges::find_if(mList, [name](HyprlandMonitor* m) {
|
||||
auto monitorIter = std::ranges::find_if(mList, [&](HyprlandMonitor* m) {
|
||||
return m->bindableName().value() == name;
|
||||
});
|
||||
|
||||
@@ -820,7 +825,7 @@ void HyprlandIpc::refreshMonitors(bool canCreate) {
|
||||
auto object = entry.toObject().toVariantMap();
|
||||
auto name = object.value("name").toString();
|
||||
|
||||
auto monitorIter = std::ranges::find_if(mList, [name](HyprlandMonitor* m) {
|
||||
auto monitorIter = std::ranges::find_if(mList, [&](HyprlandMonitor* m) {
|
||||
return m->bindableName().value() == name;
|
||||
});
|
||||
|
||||
|
||||
@@ -102,7 +102,11 @@ public:
|
||||
[[nodiscard]] ObjectModel<HyprlandToplevel>* toplevels();
|
||||
|
||||
// No byId because these preemptively create objects. The given id is set if created.
|
||||
HyprlandWorkspace* findWorkspaceByName(const QString& name, bool createIfMissing, qint32 id = -1);
|
||||
HyprlandWorkspace* findWorkspaceByName(
|
||||
const QString& name,
|
||||
bool createIfMissing,
|
||||
const QString& address = QString()
|
||||
);
|
||||
HyprlandMonitor* findMonitorByName(const QString& name, bool createIfMissing, qint32 id = -1);
|
||||
HyprlandToplevel* findToplevelByAddress(quint64 address, bool createIfMissing);
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@ void HyprlandMonitor::updateInitial(qint32 id, const QString& name, const QStrin
|
||||
|
||||
void HyprlandMonitor::updateFromObject(QVariantMap object) {
|
||||
auto activeWorkspaceObj = object.value("activeWorkspace").value<QVariantMap>();
|
||||
auto activeWorkspaceAddress = activeWorkspaceObj.value("address").value<QString>();
|
||||
auto activeWorkspaceId = activeWorkspaceObj.value("id").value<qint32>();
|
||||
if (activeWorkspaceAddress.isEmpty()) activeWorkspaceAddress = QString::number(activeWorkspaceId);
|
||||
auto activeWorkspaceName = activeWorkspaceObj.value("name").value<QString>();
|
||||
auto focused = object.value("focused").value<bool>();
|
||||
|
||||
@@ -46,7 +48,8 @@ void HyprlandMonitor::updateFromObject(QVariantMap object) {
|
||||
if (this->bActiveWorkspace == nullptr
|
||||
|| this->bActiveWorkspace->bindableName().value() != activeWorkspaceName)
|
||||
{
|
||||
auto* workspace = this->ipc->findWorkspaceByName(activeWorkspaceName, true, activeWorkspaceId);
|
||||
auto* workspace =
|
||||
this->ipc->findWorkspaceByName(activeWorkspaceName, true, activeWorkspaceAddress);
|
||||
workspace->setMonitor(this);
|
||||
this->setActiveWorkspace(workspace);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,18 @@ QVariantMap HyprlandWorkspace::lastIpcObject() const { return this->mLastIpcObje
|
||||
HyprlandWorkspace::HyprlandWorkspace(HyprlandIpc* ipc): QObject(ipc), ipc(ipc) {
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
|
||||
this->bActive.setBinding([this]() {
|
||||
this->bId.setBinding([this] {
|
||||
auto idOk = false;
|
||||
auto id = this->bAddress.value().toInt(&idOk);
|
||||
if (!idOk) id = -1;
|
||||
return id;
|
||||
});
|
||||
|
||||
this->bActive.setBinding([this] {
|
||||
return this->bMonitor.value() && this->bMonitor->bindableActiveWorkspace().value() == this;
|
||||
});
|
||||
|
||||
this->bFocused.setBinding([this]() {
|
||||
this->bFocused.setBinding([this] {
|
||||
return this->ipc->bindableFocusedWorkspace().value() == this;
|
||||
});
|
||||
|
||||
@@ -35,9 +42,9 @@ HyprlandWorkspace::HyprlandWorkspace(HyprlandIpc* ipc): QObject(ipc), ipc(ipc) {
|
||||
Qt::endPropertyUpdateGroup();
|
||||
}
|
||||
|
||||
void HyprlandWorkspace::updateInitial(qint32 id, const QString& name) {
|
||||
void HyprlandWorkspace::updateInitial(const QString& address, const QString& name) {
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
this->bId = id;
|
||||
this->bAddress = address;
|
||||
this->bName = name;
|
||||
Qt::endPropertyUpdateGroup();
|
||||
}
|
||||
@@ -47,11 +54,14 @@ void HyprlandWorkspace::updateFromObject(QVariantMap object) {
|
||||
auto monitorName = object.value("monitor").value<QString>();
|
||||
auto hasFullscreen = object.value("hasfullscreen").value<bool>();
|
||||
|
||||
auto initial = this->bId == -1;
|
||||
auto initial = this->bAddress.value().isEmpty();
|
||||
|
||||
// ID cannot be updated after creation
|
||||
// Address cannot be updated after creation
|
||||
if (initial) {
|
||||
this->bId = object.value("id").value<qint32>();
|
||||
auto address = object.value("address").value<QString>();
|
||||
auto id = object.value("id").value<qint32>();
|
||||
if (address.isEmpty()) address = QString::number(id);
|
||||
this->bAddress = address;
|
||||
}
|
||||
|
||||
// No events we currently handle give a workspace id but not a name,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <qbytearrayview.h>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qjsonobject.h>
|
||||
#include <qobject.h>
|
||||
#include <qproperty.h>
|
||||
@@ -18,6 +19,13 @@ class HyprlandMonitor;
|
||||
class HyprlandWorkspace: public QObject {
|
||||
Q_OBJECT;
|
||||
// clang-format off
|
||||
// > [!NOTE] On Hyprland versions prior to 0.57, this property is set to @@id as a string for compatibility.
|
||||
// >
|
||||
// > Prefer this property over @@id for new code.
|
||||
Q_PROPERTY(QString address READ default NOTIFY addressChanged BINDABLE bindableAddress);
|
||||
// > [!NOTE] On Hyprland versions 0.57 and later, this property is set to @@address as an integer for compatibility.
|
||||
// >
|
||||
// > After 0.57, this property will equal `-1` for non-numbered workspaces.
|
||||
Q_PROPERTY(qint32 id READ default NOTIFY idChanged BINDABLE bindableId);
|
||||
Q_PROPERTY(QString name READ default NOTIFY nameChanged BINDABLE bindableName);
|
||||
/// If this workspace is currently active on its monitor. See also @@focused.
|
||||
@@ -47,7 +55,7 @@ class HyprlandWorkspace: public QObject {
|
||||
public:
|
||||
explicit HyprlandWorkspace(HyprlandIpc* ipc);
|
||||
|
||||
void updateInitial(qint32 id, const QString& name);
|
||||
void updateInitial(const QString& address, const QString& name);
|
||||
void updateFromObject(QVariantMap object);
|
||||
|
||||
/// Activate the workspace.
|
||||
@@ -58,6 +66,7 @@ public:
|
||||
/// > ```
|
||||
Q_INVOKABLE void activate();
|
||||
|
||||
[[nodiscard]] QBindable<QString> bindableAddress() { return &this->bAddress; }
|
||||
[[nodiscard]] QBindable<qint32> bindableId() { return &this->bId; }
|
||||
[[nodiscard]] QBindable<QString> bindableName() { return &this->bName; }
|
||||
[[nodiscard]] QBindable<bool> bindableActive() { return &this->bActive; }
|
||||
@@ -75,7 +84,9 @@ public:
|
||||
void removeToplevel(HyprlandToplevel* toplevel);
|
||||
|
||||
signals:
|
||||
void addressChanged();
|
||||
void idChanged();
|
||||
void typeChanged();
|
||||
void nameChanged();
|
||||
void activeChanged();
|
||||
void focusedChanged();
|
||||
@@ -97,6 +108,7 @@ private:
|
||||
ObjectModel<HyprlandToplevel> mToplevels {this};
|
||||
|
||||
// clang-format off
|
||||
Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, QString, bAddress, &HyprlandWorkspace::addressChanged);
|
||||
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(HyprlandWorkspace, qint32, bId, -1, &HyprlandWorkspace::idChanged);
|
||||
Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, QString, bName, &HyprlandWorkspace::nameChanged);
|
||||
Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, bool, bActive, &HyprlandWorkspace::activeChanged);
|
||||
|
||||
@@ -16,7 +16,7 @@ FloatingWindow {
|
||||
color: "lightgray"
|
||||
|
||||
ColumnLayout {
|
||||
Text { text: `Workspace ${wsDelegate.modelData.id} on ${wsDelegate.modelData.monitor} | urgent: ${wsDelegate.modelData.urgent}`}
|
||||
Text { text: `Workspace (addr ${wsDelegate.modelData.address}; id ${wsDelegate.modelData.id}; name: ${wsDelegate.modelData.name}) on ${wsDelegate.modelData.monitor} | urgent: ${wsDelegate.modelData.urgent}`}
|
||||
|
||||
ColumnLayout {
|
||||
Repeater {
|
||||
|
||||
Reference in new issue
Block a user