From fb343ab639122f50769e8e86a6175625507325bd Mon Sep 17 00:00:00 2001
From: outfoxxed <outfoxxed@outfoxxed.me>
Date: Mon, 27 Jan 2025 22:19:28 -0800
Subject: [PATCH] hyprland/ipc: prefer ID based workspace lookups to name based
 ones

Should (hopefully) reduce race condition issues.
---
 src/wayland/hyprland/ipc/connection.cpp | 31 ++++++++++++++++++-------
 src/wayland/hyprland/ipc/connection.hpp |  2 +-
 src/wayland/hyprland/ipc/workspace.cpp  |  5 ++--
 3 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/src/wayland/hyprland/ipc/connection.cpp b/src/wayland/hyprland/ipc/connection.cpp
index 920dc57c..c797b609 100644
--- a/src/wayland/hyprland/ipc/connection.cpp
+++ b/src/wayland/hyprland/ipc/connection.cpp
@@ -342,7 +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();
+			qCDebug(logHyprlandIpc) << "Workspace" << id << "activated on"
+			                        << this->mFocusedMonitor->name();
 		}
 	} else if (event->name == "moveworkspacev2") {
 		auto args = event->parseView(3);
@@ -377,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);
@@ -414,9 +428,8 @@ void HyprlandIpc::refreshWorkspaces(bool canCreate) {
 
 			auto id = object.value("id").toInt();
 
-			auto workspaceIter = std::ranges::find_if(mList, [&](const HyprlandWorkspace* m) {
-				return m->id() == id;
-			});
+			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.
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<HyprlandWorkspace>* 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/workspace.cpp b/src/wayland/hyprland/ipc/workspace.cpp
index a11acb34..428edd6b 100644
--- a/src/wayland/hyprland/ipc/workspace.cpp
+++ b/src/wayland/hyprland/ipc/workspace.cpp
@@ -35,6 +35,7 @@ void HyprlandWorkspace::updateInitial(qint32 id, QString name) {
 }
 
 void HyprlandWorkspace::updateFromObject(QVariantMap object) {
+	auto name = object.value("name").value<QString>();
 	auto monitorId = object.value("monitorID").value<qint32>();
 	auto monitorName = object.value("monitor").value<QString>();
 
@@ -48,8 +49,8 @@ void HyprlandWorkspace::updateFromObject(QVariantMap object) {
 
 	// 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) {
-		this->mName = object.value("name").value<QString>();
+	if (initial && name != this->mName) {
+		this->mName = name;
 		emit this->nameChanged();
 	}