diff --git a/src/wayland/hyprland/ipc/connection.cpp b/src/wayland/hyprland/ipc/connection.cpp index 794ecff6..c797b609 100644 --- a/src/wayland/hyprland/ipc/connection.cpp +++ b/src/wayland/hyprland/ipc/connection.cpp @@ -333,6 +333,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) { auto* monitor = this->findMonitorByName(name, true); this->setFocusedMonitor(monitor); monitor->setActiveWorkspace(workspace); + qCDebug(logHyprlandIpc) << "Monitor" << name << "focused with workspace" << workspace->id(); } else if (event->name == "workspacev2") { auto args = event->parseView(2); auto id = args.at(0).toInt(); @@ -341,6 +342,8 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) { if (this->mFocusedMonitor != nullptr) { auto* workspace = this->findWorkspaceByName(name, true, id); this->mFocusedMonitor->setActiveWorkspace(workspace); + qCDebug(logHyprlandIpc) << "Workspace" << id << "activated on" + << this->mFocusedMonitor->name(); } } else if (event->name == "moveworkspacev2") { auto args = event->parseView(3); @@ -351,6 +354,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) { auto* workspace = this->findWorkspaceByName(name, true, id); auto* monitor = this->findMonitorByName(monitorName, true); + qCDebug(logHyprlandIpc) << "Workspace" << id << "moved to monitor" << monitorName; workspace->setMonitor(monitor); } else if (event->name == "renameworkspace") { auto args = event->parseView(2); @@ -374,15 +378,28 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) { HyprlandWorkspace* HyprlandIpc::findWorkspaceByName(const QString& name, bool createIfMissing, qint32 id) { const auto& mList = this->mWorkspaces.valueList(); + HyprlandWorkspace* workspace = nullptr; - auto workspaceIter = - std::ranges::find_if(mList, [name](const HyprlandWorkspace* m) { return m->name() == name; }); + if (id != -1) { + auto workspaceIter = + std::ranges::find_if(mList, [&](const HyprlandWorkspace* m) { return m->id() == id; }); - if (workspaceIter != mList.end()) { - return *workspaceIter; + workspace = workspaceIter == mList.end() ? nullptr : *workspaceIter; + } + + if (!workspace) { + auto workspaceIter = + std::ranges::find_if(mList, [&](const HyprlandWorkspace* m) { return m->name() == name; }); + + workspace = workspaceIter == mList.end() ? nullptr : *workspaceIter; + } + + if (workspace) { + return workspace; } else if (createIfMissing) { qCDebug(logHyprlandIpc) << "Workspace" << name - << "requested before creation, performing early init"; + << "requested before creation, performing early init with id" << id; + auto* workspace = new HyprlandWorkspace(this); workspace->updateInitial(id, name); this->mWorkspaces.insertObject(workspace); @@ -400,24 +417,34 @@ void HyprlandIpc::refreshWorkspaces(bool canCreate) { this->requestingWorkspaces = false; if (!success) return; - qCDebug(logHyprlandIpc) << "parsing workspaces response"; + qCDebug(logHyprlandIpc) << "Parsing workspaces response"; auto json = QJsonDocument::fromJson(resp).array(); const auto& mList = this->mWorkspaces.valueList(); - auto names = QVector(); + auto ids = QVector(); for (auto entry: json) { auto object = entry.toObject().toVariantMap(); - auto name = object.value("name").toString(); - auto workspaceIter = std::ranges::find_if(mList, [name](const HyprlandWorkspace* m) { - return m->name() == name; - }); + auto id = object.value("id").toInt(); + + auto workspaceIter = + std::ranges::find_if(mList, [&](const HyprlandWorkspace* m) { return m->id() == id; }); + + // Only fall back to name-based filtering as a last resort, for workspaces where + // no ID has been determined yet. + if (workspaceIter == mList.end()) { + auto name = object.value("name").toString(); + + workspaceIter = std::ranges::find_if(mList, [&](const HyprlandWorkspace* m) { + return m->id() == -1 && m->name() == name; + }); + } auto* workspace = workspaceIter == mList.end() ? nullptr : *workspaceIter; auto existed = workspace != nullptr; - if (workspace == nullptr) { + if (!existed) { if (!canCreate) continue; workspace = new HyprlandWorkspace(this); } @@ -428,20 +455,22 @@ void HyprlandIpc::refreshWorkspaces(bool canCreate) { this->mWorkspaces.insertObject(workspace); } - names.push_back(name); + ids.push_back(id); } - auto removedWorkspaces = QVector(); + if (canCreate) { + auto removedWorkspaces = QVector(); - for (auto* workspace: mList) { - if (!names.contains(workspace->name())) { - removedWorkspaces.push_back(workspace); + for (auto* workspace: mList) { + if (!ids.contains(workspace->id())) { + removedWorkspaces.push_back(workspace); + } } - } - for (auto* workspace: removedWorkspaces) { - this->mWorkspaces.removeObject(workspace); - delete workspace; + for (auto* workspace: removedWorkspaces) { + this->mWorkspaces.removeObject(workspace); + delete workspace; + } } }); } diff --git a/src/wayland/hyprland/ipc/connection.hpp b/src/wayland/hyprland/ipc/connection.hpp index 856d4173..287b1ee8 100644 --- a/src/wayland/hyprland/ipc/connection.hpp +++ b/src/wayland/hyprland/ipc/connection.hpp @@ -81,7 +81,7 @@ public: [[nodiscard]] ObjectModel* workspaces(); // No byId because these preemptively create objects. The given id is set if created. - HyprlandWorkspace* findWorkspaceByName(const QString& name, bool createIfMissing, qint32 id = 0); + HyprlandWorkspace* findWorkspaceByName(const QString& name, bool createIfMissing, qint32 id = -1); HyprlandMonitor* findMonitorByName(const QString& name, bool createIfMissing, qint32 id = -1); // canCreate avoids making ghost workspaces when the connection races diff --git a/src/wayland/hyprland/ipc/monitor.cpp b/src/wayland/hyprland/ipc/monitor.cpp index 8ee5e207..190ab668 100644 --- a/src/wayland/hyprland/ipc/monitor.cpp +++ b/src/wayland/hyprland/ipc/monitor.cpp @@ -117,6 +117,8 @@ void HyprlandMonitor::setActiveWorkspace(HyprlandWorkspace* workspace) { this->mActiveWorkspace = workspace; if (workspace != nullptr) { + workspace->setMonitor(this); + QObject::connect( workspace, &QObject::destroyed, diff --git a/src/wayland/hyprland/ipc/workspace.cpp b/src/wayland/hyprland/ipc/workspace.cpp index 153dea6b..428edd6b 100644 --- a/src/wayland/hyprland/ipc/workspace.cpp +++ b/src/wayland/hyprland/ipc/workspace.cpp @@ -35,18 +35,22 @@ void HyprlandWorkspace::updateInitial(qint32 id, QString name) { } void HyprlandWorkspace::updateFromObject(QVariantMap object) { - auto id = object.value("id").value(); auto name = object.value("name").value(); auto monitorId = object.value("monitorID").value(); auto monitorName = object.value("monitor").value(); - if (id != this->mId) { - this->mId = id; + auto initial = this->mId = -1; + + // ID cannot be updated after creation + if (initial) { + this->mId = object.value("id").value(); emit this->idChanged(); } - if (name != this->mName) { - this->mName = std::move(name); + // No events we currently handle give a workspace id but not a name, + // so we shouldn't set this if it isn't an initial query + if (initial && name != this->mName) { + this->mName = name; emit this->nameChanged(); }