dc5a0224ce627436aba2e525d46767b1130f0868

Author
outfoxxed <outfoxxed@outfoxxed.me>
Committer
outfoxxed <outfoxxed@outfoxxed.me>
Date

Message

fixup: fix breakage after hyprland#5762 + improve Hy3NodeData repr

Hy3NodeData now uses std::variant instead of a union, and all accesses
are checked.

Diff

This diff is truncated to protect this page.

  1diff --git a/flake.lock b/flake.lock
  2index 7b5053a9032a0c4df47d62a67b6930d0fad69f17..50d25e8b369ad5a33619a9e93c82c8a5c4c7f881 100644
  3--- a/flake.lock
  4+++ b/flake.lock
  5@@ -41,16 +41,15 @@
  6         "xdph": "xdph"
  7       },
  8       "locked": {
  9-        "lastModified": 1713819507,
 10-        "narHash": "sha256-mG0kD8HmkCsHRkTBmqGb/QvM1vAvkAmB5p2H5XQ0zLo=",
 11+        "lastModified": 1714409407,
 12+        "narHash": "sha256-gc10Ag5O/s+EE24JZUXTpH4ogRsSyAfdgr7OvSNhQ8s=",
 13         "owner": "hyprwm",
 14         "repo": "Hyprland",
 15-        "rev": "da839f20f1b1a57ec78d6b041f8d1369150d253e",
 16+        "rev": "f2b03e9679bc1a091fecffd98b50a4179b5c7d43",
 17         "type": "github"
 18       },
 19       "original": {
 20         "owner": "hyprwm",
 21-        "ref": "da839f20f1b1a57ec78d6b041f8d1369150d253e",
 22         "repo": "Hyprland",
 23         "type": "github"
 24       }
 25@@ -117,11 +116,11 @@
 26         ]
 27       },
 28       "locked": {
 29-        "lastModified": 1713730424,
 30-        "narHash": "sha256-MqYjTvQCxwuDIuAaqIEuyqserm5PYr0auiHHPuYFN/k=",
 31+        "lastModified": 1714171579,
 32+        "narHash": "sha256-eaWDIvt8ufUKKz3Lc2a3PyemLJG1m9RYlF+HP3hWbaw=",
 33         "owner": "hyprwm",
 34         "repo": "hyprwayland-scanner",
 35-        "rev": "7c9c8adfe7e424faa0c5ce6e8be90157fda53c06",
 36+        "rev": "126dad854f22fe30e6b82cd21808e76903d90ac5",
 37         "type": "github"
 38       },
 39       "original": {
 40@@ -132,11 +131,11 @@
 41     },
 42     "nixpkgs": {
 43       "locked": {
 44-        "lastModified": 1713537308,
 45-        "narHash": "sha256-XtTSSIB2DA6tOv+l0FhvfDMiyCmhoRbNB+0SeInZkbk=",
 46+        "lastModified": 1713714899,
 47+        "narHash": "sha256-+z/XjO3QJs5rLE5UOf015gdVauVRQd2vZtsFkaXBq2Y=",
 48         "owner": "NixOS",
 49         "repo": "nixpkgs",
 50-        "rev": "5c24cf2f0a12ad855f444c30b2421d044120c66f",
 51+        "rev": "6143fc5eeb9c4f00163267708e26191d1e918932",
 52         "type": "github"
 53       },
 54       "original": {
 55diff --git a/flake.nix b/flake.nix
 56index 998ea51b4e10419c90ba98e1f5ef4f6fdca07d4a..a375308b98bc8c5caa8e69646dfbab4ef69e3f70 100644
 57--- a/flake.nix
 58+++ b/flake.nix
 59@@ -1,6 +1,6 @@
 60 {
 61   inputs = {
 62-    hyprland.url = "github:hyprwm/Hyprland?ref=da839f20f1b1a57ec78d6b041f8d1369150d253e";
 63+    hyprland.url = "github:hyprwm/Hyprland";
 64   };
 65 
 66   outputs = { self, hyprland, ... }: let
 67diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
 68index ca60aa0bbd209df896614742cead2affe3afc46e..4bfcca88ec666db3d74d6db0eecabec2916c4ba9 100644
 69--- a/src/Hy3Layout.cpp
 70+++ b/src/Hy3Layout.cpp
 71@@ -3,6 +3,7 @@
 72 
 73 #include <hyprland/src/Compositor.hpp>
 74 #include <hyprland/src/desktop/DesktopTypes.hpp>
 75+#include <hyprland/src/desktop/Workspace.hpp>
 76 #include <hyprland/src/plugins/PluginAPI.hpp>
 77 #include <ranges>
 78 
 79@@ -15,18 +16,18 @@ std::shared_ptr<HOOK_CALLBACK_FN> windowTitleHookPtr;
 80 std::shared_ptr<HOOK_CALLBACK_FN> urgentHookPtr;
 81 std::shared_ptr<HOOK_CALLBACK_FN> tickHookPtr;
 82 
 83-bool performContainment(Hy3Node& node, bool contained, CWindow* window) {
 84-	if (node.data.type == Hy3NodeType::Group) {
 85-		auto& group = node.data.as_group;
 86+bool performContainment(Hy3Node& node, bool contained, PHLWINDOW& window) {
 87+	if (node.data.is_group()) {
 88+		auto& group = node.data.as_group();
 89 		contained |= group.containment;
 90 
 91-		auto iter = node.data.as_group.children.begin();
 92-		while (iter != node.data.as_group.children.end()) {
 93-			switch ((*iter)->data.type) {
 94+		auto iter = group.children.begin();
 95+		while (iter != group.children.end()) {
 96+			switch ((*iter)->data.type()) {
 97 			case Hy3NodeType::Group: return performContainment(**iter, contained, window);
 98 			case Hy3NodeType::Window:
 99 				if (contained) {
100-					auto wpid = (*iter)->data.as_window->getPID();
101+					auto wpid = (*iter)->data.as_window()->getPID();
102 					auto ppid = getPPIDof(window->getPID());
103 					while (ppid > 10) { // `> 10` yoinked from HL swallow
104 						if (ppid == wpid) {
105@@ -58,7 +59,7 @@ bool performContainment(Hy3Node& node, bool contained, CWindow* window) {
106 	return false;
107 }
108 
109-void Hy3Layout::onWindowCreated(CWindow* window, eDirection direction) {
110+void Hy3Layout::onWindowCreated(PHLWINDOW window, eDirection direction) {
111 	for (auto& node: this->nodes) {
112 		if (node.parent == nullptr && performContainment(node, false, window)) {
113 			return;
114@@ -68,11 +69,11 @@ void Hy3Layout::onWindowCreated(CWindow* window, eDirection direction) {
115 	IHyprLayout::onWindowCreated(window, direction);
116 }
117 
118-void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) {
119+void Hy3Layout::onWindowCreatedTiling(PHLWINDOW window, eDirection) {
120 	hy3_log(
121 	    LOG,
122 	    "onWindowCreatedTiling called with window {:x} (floating: {}, monitor: {}, workspace: {})",
123-	    (uintptr_t) window,
124+	    (uintptr_t) window.get(),
125 	    window->m_bIsFloating,
126 	    window->m_iMonitorID,
127 	    window->m_pWorkspace->m_iID
128@@ -85,7 +86,7 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) {
129 		hy3_log(
130 		    ERR,
131 		    "onWindowCreatedTiling called with a window ({:x}) that is already tiled (node: {:x})",
132-		    (uintptr_t) window,
133+		    (uintptr_t) window.get(),
134 		    (uintptr_t) existing
135 		);
136 		return;
137@@ -142,16 +143,15 @@ void Hy3Layout::insertNode(Hy3Node& node) {
138 	}
139 
140 	if (opening_after == nullptr) {
141-		if (g_pCompositor->m_pLastWindow != nullptr
142-		    && g_pCompositor->m_pLastWindow->m_pWorkspace == node.workspace
143-		    && !g_pCompositor->m_pLastWindow->m_bIsFloating
144-		    && (node.data.type == Hy3NodeType::Window
145-		        || g_pCompositor->m_pLastWindow != node.data.as_window)
146-		    && g_pCompositor->m_pLastWindow->m_bIsMapped)
147+		auto last_window = g_pCompositor->m_pLastWindow.lock();
148+		if (last_window != nullptr && last_window->m_pWorkspace == node.workspace
149+		    && !last_window->m_bIsFloating
150+		    && (node.data.is_window() || last_window != node.data.as_window())
151+		    && last_window->m_bIsMapped)
152 		{
153-			opening_after = this->getNodeFromWindow(g_pCompositor->m_pLastWindow);
154+			opening_after = this->getNodeFromWindow(last_window);
155 		} else {
156-			auto* mouse_window = g_pCompositor->vectorToWindowUnified(
157+			auto mouse_window = g_pCompositor->vectorToWindowUnified(
158 			    g_pInputManager->getMouseCoordsInternal(),
159 			    RESERVED_EXTENTS | INPUT_EXTENTS
160 			);
161@@ -163,8 +163,8 @@ void Hy3Layout::insertNode(Hy3Node& node) {
162 	}
163 
164 	if (opening_after != nullptr
165-	    && ((node.data.type == Hy3NodeType::Group
166-	         && (opening_after == &node || node.data.as_group.hasChild(opening_after)))
167+	    && ((node.data.is_group()
168+	         && (opening_after == &node || node.data.as_group().hasChild(opening_after)))
169 	        || opening_after->reparenting))
170 	{
171diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
172index 3bb27de013ecf7950064be894017e61eb6586411..79162612d23e378f72100b7bcd66f9067af8875c 100644
173--- a/src/Hy3Layout.hpp
174+++ b/src/Hy3Layout.hpp
175@@ -70,30 +70,30 @@ enum class ExpandFullscreenOption {
176 
177 class Hy3Layout: public IHyprLayout {
178 public:
179-	virtual void onWindowCreated(CWindow*, eDirection = DIRECTION_DEFAULT);
180-	virtual void onWindowCreatedTiling(CWindow*, eDirection = DIRECTION_DEFAULT);
181-	virtual void onWindowRemovedTiling(CWindow*);
182-	virtual void onWindowFocusChange(CWindow*);
183-	virtual bool isWindowTiled(CWindow*);
184-	virtual void recalculateMonitor(const int& monitor_id);
185-	virtual void recalculateWindow(CWindow*);
186-	virtual void
187-	resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow = nullptr);
188-	virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool enable_fullscreen);
189-	virtual std::any layoutMessage(SLayoutMessageHeader header, std::string content);
190-	virtual SWindowRenderLayoutHints requestRenderHints(CWindow*);
191-	virtual void switchWindows(CWindow*, CWindow*);
192-	virtual void moveWindowTo(CWindow*, const std::string& direction, bool silent);
193-	virtual void alterSplitRatio(CWindow*, float, bool);
194-	virtual std::string getLayoutName();
195-	virtual CWindow* getNextWindowCandidate(CWindow*);
196-	virtual void replaceWindowDataWith(CWindow* from, CWindow* to);
197-	virtual bool isWindowReachable(CWindow*);
198-	virtual void bringWindowToTop(CWindow*);
199-	virtual Vector2D predictSizeForNewWindowTiled() { return Vector2D(); }
200-
201-	virtual void onEnable();
202-	virtual void onDisable();
203+	void onWindowCreated(PHLWINDOW, eDirection = DIRECTION_DEFAULT) override;
204+	void onWindowCreatedTiling(PHLWINDOW, eDirection = DIRECTION_DEFAULT) override;
205+	void onWindowRemovedTiling(PHLWINDOW) override;
206+	void onWindowFocusChange(PHLWINDOW) override;
207+	bool isWindowTiled(PHLWINDOW) override;
208+	void recalculateMonitor(const int& monitor_id) override;
209+	void recalculateWindow(PHLWINDOW) override;
210+	void resizeActiveWindow(const Vector2D& delta, eRectCorner corner, PHLWINDOW pWindow = nullptr)
211+	    override;
212+	void fullscreenRequestForWindow(PHLWINDOW, eFullscreenMode, bool enable_fullscreen) override;
213+	std::any layoutMessage(SLayoutMessageHeader header, std::string content) override;
214+	SWindowRenderLayoutHints requestRenderHints(PHLWINDOW) override;
215+	void switchWindows(PHLWINDOW, PHLWINDOW) override;
216+	void moveWindowTo(PHLWINDOW, const std::string& direction, bool silent) override;
217+	void alterSplitRatio(PHLWINDOW, float, bool) override;
218+	std::string getLayoutName() override;
219+	PHLWINDOW getNextWindowCandidate(PHLWINDOW) override;
220+	void replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to) override;
221+	bool isWindowReachable(PHLWINDOW) override;
222+	void bringWindowToTop(PHLWINDOW) override;
223+	Vector2D predictSizeForNewWindowTiled() override { return Vector2D(); }
224+
225+	void onEnable() override;
226+	void onDisable() override;
227 
228 	void insertNode(Hy3Node& node);
229 	void makeGroupOnWorkspace(const PHLWORKSPACE& workspace, Hy3GroupLayout, GroupEphemeralityOption);
230@@ -126,7 +126,7 @@ public:
231 	void killFocusedNode(const PHLWORKSPACE& workspace);
232 	void expand(const PHLWORKSPACE& workspace, ExpandOption, ExpandFullscreenOption);
233 
234-	bool shouldRenderSelected(CWindow*);
235+	bool shouldRenderSelected(const PHLWINDOW&);
236 
237 	Hy3Node* getWorkspaceRootGroup(const PHLWORKSPACE& workspace);
238 	Hy3Node* getWorkspaceFocusedNode(
239@@ -144,7 +144,7 @@ public:
240 	std::list<Hy3TabGroup> tab_groups;
241 
242 private:
243-	Hy3Node* getNodeFromWindow(CWindow*);
244+	Hy3Node* getNodeFromWindow(const PHLWINDOW&);
245 	void applyNodeDataToWindow(Hy3Node*, bool no_animation = false);
246 
247 	// if shift is true, shift the window in the given direction, returning
248diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp
249index 9572d7bacfa7c720dc03663d0d30d18cd3937cbb..04ef6f54c2ea31884840dbfc88a889cf06a3e2ec 100644
250--- a/src/Hy3Node.cpp
251+++ b/src/Hy3Node.cpp
252@@ -1,6 +1,10 @@
253 #include <sstream>
254+#include <stdexcept>
255+#include <variant>
256 
257+#include <bits/ranges_util.h>
258 #include <hyprland/src/Compositor.hpp>
259+#include <hyprland/src/defines.hpp>
260 #include <hyprland/src/helpers/Box.hpp>
261 #include <hyprland/src/plugins/PluginAPI.hpp>
262 
263@@ -37,8 +41,8 @@ bool Hy3GroupData::hasChild(Hy3Node* node) {
264 	for (auto child: this->children) {
265 		if (child == node) return true;
266 
267-		if (child->data.type == Hy3NodeType::Group) {
268-			if (child->data.as_group.hasChild(node)) return true;
269+		if (child->data.is_group()) {
270+			if (child->data.as_group().hasChild(node)) return true;
271 		}
272 	}
273 
274@@ -51,11 +55,10 @@ void Hy3GroupData::collapseExpansions() {
275 
276 	Hy3Node* node = this->focused_child;
277 
278-	while (node->data.type == Hy3NodeType::Group
279-	       && node->data.as_group.expand_focused == ExpandFocusType::Stack)
280-	{
281-		node->data.as_group.expand_focused = ExpandFocusType::NotExpanded;
282-		node = node->data.as_group.focused_child;
283+	while (node->data.is_group() && node->data.as_group().expand_focused == ExpandFocusType::Stack) {
284+		auto& group = node->data.as_group();
285+		group.expand_focused = ExpandFocusType::NotExpanded;
286+		node = group.focused_child;
287 	}
288 }
289 
290@@ -79,63 +82,82 @@ void Hy3GroupData::setEphemeral(GroupEphemeralityOption ephemeral) {
291 
292 // Hy3NodeData //
293 
294-Hy3NodeData::Hy3NodeData(): Hy3NodeData((CWindow*) nullptr) {}
295-
296-Hy3NodeData::Hy3NodeData(CWindow* window): type(Hy3NodeType::Window) { this->as_window = window; }
297-
298-Hy3NodeData::Hy3NodeData(Hy3GroupLayout layout): Hy3NodeData(Hy3GroupData(layout)) {}
299+Hy3NodeData::Hy3NodeData(Hy3GroupLayout layout) { this->data.emplace<1>(layout); }
300 
301-Hy3NodeData::Hy3NodeData(Hy3GroupData group): type(Hy3NodeType::Group) {
302-	new (&this->as_group) Hy3GroupData(std::move(group));
303-}
304-
305-Hy3NodeData::Hy3NodeData(Hy3NodeData&& from): type(from.type) {
306-	switch (from.type) {
307-	case Hy3NodeType::Window: this->as_window = from.as_window; break;
308-	case Hy3NodeType::Group: new (&this->as_group) Hy3GroupData(std::move(from.as_group)); break;
309-	}
310-}
311+Hy3NodeData::Hy3NodeData(PHLWINDOW window) { this->data.emplace<0>(window); }
312 
313-Hy3NodeData::~Hy3NodeData() {
314-	switch (this->type) {
315-	case Hy3NodeType::Window: break;
316-	case Hy3NodeType::Group:
317-		this->as_group.~Hy3GroupData();
318+Hy3NodeData::Hy3NodeData(Hy3GroupData group) { this->data.emplace<1>(std::move(group)); }
319 
320-		// who ever thought calling the dtor after a move was a good idea?
321-		this->type = Hy3NodeType::Window;
322-		break;
323+Hy3NodeData::Hy3NodeData(Hy3NodeData&& node) {
324+	if (std::holds_alternative<PHLWINDOWREF>(node.data)) {
325+		this->data.emplace<0>(std::get<PHLWINDOWREF>(node.data));
326+	} else if (std::holds_alternative<Hy3GroupData>(node.data)) {
327+		this->data.emplace<1>(std::move(std::get<Hy3GroupData>(node.data)));
328 	}
329 }
330 
331-Hy3NodeData& Hy3NodeData::operator=(CWindow* window) {
332+Hy3NodeData& Hy3NodeData::operator=(PHLWINDOW window) {
333 	*this = Hy3NodeData(window);
334-
335 	return *this;
336 }
337 
338 Hy3NodeData& Hy3NodeData::operator=(Hy3GroupLayout layout) {
339 	*this = Hy3NodeData(layout);
340-
341 	return *this;
342 }
343 
344 Hy3NodeData& Hy3NodeData::operator=(Hy3NodeData&& from) {
345-	if (this->type == Hy3NodeType::Group) {
346-		this->as_group.~Hy3GroupData();
347-	}
348+	this->~Hy3NodeData();
349+	new (this) Hy3NodeData(std::move(from));
350+	return *this;
351+}
352diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp
353index 772d01fc612569225a3849477cbfdbccf623d5d0..7a52f3c83ac9e2fb58a938dbeb4a1be301057770 100644
354--- a/src/Hy3Node.hpp
355+++ b/src/Hy3Node.hpp
356@@ -4,8 +4,9 @@ struct Hy3Node;
357 struct Hy3GroupData;
358 enum class Hy3GroupLayout;
359 
360-#include <list>
361+#include <variant>
362 
363+#include <hyprland/src/defines.hpp>
364 #include <hyprland/src/desktop/Window.hpp>
365 
366 #include "Hy3Layout.hpp"
367@@ -47,7 +48,6 @@ struct Hy3GroupData {
368 	void setLayout(Hy3GroupLayout layout);
369 	void setEphemeral(GroupEphemeralityOption ephemeral);
370 
371-private:
372 	Hy3GroupData(Hy3GroupData&&);
373 	Hy3GroupData(const Hy3GroupData&) = delete;
374 
375@@ -56,26 +56,28 @@ private:
376 
377 class Hy3NodeData {
378 public:
379-	Hy3NodeType type;
380-	union {
381-		Hy3GroupData as_group;
382-		CWindow* as_window;
383-	};
384-
385-	Hy3NodeData();
386-	Hy3NodeData(CWindow* window);
387+	Hy3NodeData() = default;
388+	Hy3NodeData(Hy3GroupData);
389+	Hy3NodeData(PHLWINDOW window);
390 	Hy3NodeData(Hy3GroupLayout layout);
391-	~Hy3NodeData();
392+	Hy3NodeData(Hy3NodeData&&);
393+	~Hy3NodeData() = default;
394 
395-	Hy3NodeData& operator=(CWindow*);
396+	Hy3NodeData& operator=(PHLWINDOW);
397 	Hy3NodeData& operator=(Hy3GroupLayout);
398+	Hy3NodeData& operator=(Hy3NodeData&&);
399 
400 	bool operator==(const Hy3NodeData&) const;
401 
402-	// private: - I give up, C++ wins
403-	Hy3NodeData(Hy3GroupData);
404-	Hy3NodeData(Hy3NodeData&&);
405-	Hy3NodeData& operator=(Hy3NodeData&&);
406+	bool valid() const;
407+	Hy3NodeType type() const;
408+	bool is_window() const;
409+	bool is_group() const;
410+	Hy3GroupData& as_group();
411+	PHLWINDOW as_window();
412+
413+private:
414+	std::variant<PHLWINDOWREF, Hy3GroupData> data;
415 };
416 
417 struct Hy3Node {
418@@ -95,7 +97,7 @@ struct Hy3Node {
419 
420 	void focus();
421 	void focusWindow();
422-	CWindow* bringToTop();
423+	PHLWINDOW bringToTop();
424 	void markFocused();
425 	void raiseToTop();
426 	Hy3Node* getFocusedNode(bool ignore_group_focus = false, bool stop_at_expanded = false);
427@@ -115,7 +117,7 @@ struct Hy3Node {
428 	void setHidden(bool);
429 
430 	Hy3Node* findNodeForTabGroup(Hy3TabGroup&);
431-	void appendAllWindows(std::vector<CWindow*>&);
432+	void appendAllWindows(std::vector<PHLWINDOW>&);
433 	std::string debugNode();
434 
435 	// Remove this node from its parent, deleting the parent if it was
436diff --git a/src/SelectionHook.cpp b/src/SelectionHook.cpp
437index 603ede6f8069f8e2c383c5550a557274ad08c249..ae5c0519a1f9178d97ed715f393a68fad6b442e0 100644
438--- a/src/SelectionHook.cpp
439+++ b/src/SelectionHook.cpp
440@@ -6,15 +6,15 @@
441 namespace selection_hook {
442 inline CFunctionHook* g_LastSelectionHook = nullptr;
443 
444-void hook_updateDecos(void* thisptr, CWindow* window) {
445+void hook_updateDecos(void* thisptr, PHLWINDOW window) {
446 	bool explicitly_selected = g_Hy3Layout->shouldRenderSelected(window);
447 
448-	auto* lastWindow = g_pCompositor->m_pLastWindow;
449+	auto lastWindow = g_pCompositor->m_pLastWindow;
450 	if (explicitly_selected) {
451 		g_pCompositor->m_pLastWindow = window;
452 	}
453 
454-	((void (*)(void*, CWindow*)) g_LastSelectionHook->m_pOriginal)(thisptr, window);
455+	((void (*)(void*, PHLWINDOW)) g_LastSelectionHook->m_pOriginal)(thisptr, window);
456 
457 	if (explicitly_selected) {
458 		g_pCompositor->m_pLastWindow = lastWindow;
459diff --git a/src/TabGroup.cpp b/src/TabGroup.cpp
460index d8005b49a3a827147b4fc33dba2e2770a9088451..86eb6791cd6115869b0705bcd27c2c23cf3c8f65 100644
461--- a/src/TabGroup.cpp
462+++ b/src/TabGroup.cpp
463@@ -2,6 +2,8 @@
464 
465 #include <cairo/cairo.h>
466 #include <hyprland/src/Compositor.hpp>
467+#include <hyprland/src/desktop/DesktopTypes.hpp>
468+#include <hyprland/src/desktop/Workspace.hpp>
469 #include <hyprland/src/helpers/Box.hpp>
470 #include <hyprland/src/helpers/Color.hpp>
471 #include <hyprland/src/render/OpenGL.hpp>
472@@ -328,7 +330,7 @@ exitloop:
473 
474 		// set stats from node data
475 		auto* parent = (*node)->parent;
476-		auto& parent_group = parent->data.as_group;
477+		auto& parent_group = parent->data.as_group();
478 
479 		entry->setFocused(
480 		    parent_group.focused_child == *node
481@@ -433,11 +435,11 @@ void Hy3TabGroup::updateWithGroup(Hy3Node& node, bool warp) {
482 		if (warp) this->size.warp();
483 	}
484 
485-	this->bar.updateNodeList(node.data.as_group.children);
486+	this->bar.updateNodeList(node.data.as_group().children);
487 	this->bar.updateAnimations(warp);
488 
489-	if (node.data.as_group.focused_child != nullptr) {
490-		this->updateStencilWindows(*node.data.as_group.focused_child);
491+	if (node.data.as_group().focused_child != nullptr) {
492+		this->updateStencilWindows(*node.data.as_group().focused_child);
493 	}
494 }
495 
496@@ -539,8 +541,9 @@ void Hy3TabGroup::renderTabBar() {
497 
498 		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
499 
500-		for (auto* window: this->stencil_windows) {
501-			if (!g_pCompositor->windowExists(window)) continue;
502+		for (auto windowref: this->stencil_windows) {
503+			if (!valid(windowref)) continue;
504+			auto window = windowref.lock();
505 
506 			auto wpos = window->m_vRealPosition.value() - monitor->vecPosition;
507 			auto wsize = window->m_vRealSize.value();
508@@ -603,11 +606,11 @@ void Hy3TabGroup::renderTabBar() {
509 	}
510 }
511 
512-void findOverlappingWindows(Hy3Node& node, float height, std::vector<CWindow*>& windows) {
513-	switch (node.data.type) {
514-	case Hy3NodeType::Window: windows.push_back(node.data.as_window); break;
515+void findOverlappingWindows(Hy3Node& node, float height, std::vector<PHLWINDOWREF>& windows) {
516+	switch (node.data.type()) {
517+	case Hy3NodeType::Window: windows.push_back(node.data.as_window()); break;
518 	case Hy3NodeType::Group:
519-		auto& group = node.data.as_group;
520+		auto& group = node.data.as_group();
521 
522 		switch (group.layout) {
523 		case Hy3GroupLayout::SplitH:
524diff --git a/src/TabGroup.hpp b/src/TabGroup.hpp
525index 3d776243c9bdfc7b2c9c6e446dd95da8ab593818..12117f35f90aa2c45e288cb6b94159abe39f908d 100644
526--- a/src/TabGroup.hpp
527+++ b/src/TabGroup.hpp
528@@ -85,7 +85,7 @@ private:
529 
530 class Hy3TabGroup {
531 public:
532-	CWindow* target_window = nullptr;
533+	PHLWINDOW target_window = nullptr;
534 	PHLWORKSPACE workspace = nullptr;
535 	bool hidden = false;
536 	Hy3TabBar bar;
537@@ -102,7 +102,7 @@ public:
538 	void renderTabBar();
539 
540 private:
541-	std::vector<CWindow*> stencil_windows;
542+	std::vector<PHLWINDOWREF> stencil_windows;
543 	Vector2D last_pos;
544 	Vector2D last_size;
545