9b570f066a1497460dbb04ccb3dbebd833fda78b

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

Message

Raising focus and grouped movement

Diff

This diff is truncated to protect this page.

  1diff --git a/CMakeLists.txt b/CMakeLists.txt
  2index 7a7a9d412f49290dfbb8b50bdef6c16af9dba30c..1aa891436d1e7da060c94f3080dd8d05c0699c98 100644
  3--- a/CMakeLists.txt
  4+++ b/CMakeLists.txt
  5@@ -20,6 +20,7 @@ pkg_check_modules(DEPS REQUIRED pixman-1 libdrm)
  6 add_library(hy3 SHARED
  7 	src/main.cpp
  8 	src/Hy3Layout.cpp
  9+	src/SelectionHook.cpp
 10 )
 11 
 12 target_include_directories(hy3 PRIVATE
 13diff --git a/README.md b/README.md
 14index 99c0c1247448488c98ee4f845d4b8988b01d309a..45331b8621abfdcf24099e3280dcb21b3f13664b 100644
 15--- a/README.md
 16+++ b/README.md
 17@@ -6,7 +6,7 @@ i3 / sway like layout for [hyprland](https://github.com/hyprwm/hyprland).
 18 - [x] Window splits
 19 - [x] Window movement
 20 - [x] Window resizing
 21-- [ ] Selecting a group of windows at once (and related movement)
 22+- [x] Selecting a group of windows at once (and related movement)
 23 - [ ] Tabbed groups
 24 - [ ] Some convenience dispatchers not found in i3 or sway
 25 
 26@@ -35,6 +35,7 @@ You can use `hy3:makegroup` to create a new split.
 27  - `hy3:makegroup, <h | v>` - make a vertical or horizontal split
 28  - `hy3:movefocus, <l | u | d | r>` - move the focus left, up, down, or right
 29  - `hy3:movewindow, <l | u | d | r>` - move a window left, up, down, or right
 30+ - `hy3:raisefocus` - raise the active focus one level
 31  - `hy3:debugnodes` - print the node tree into the hyprland log
 32 
 33 ## Installing
 34diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
 35index 9e286fc79ed3793436a12d7d0568e0f84f0f069a..682d52798a7077fec975860839126f7a56dec320 100644
 36--- a/src/Hy3Layout.cpp
 37+++ b/src/Hy3Layout.cpp
 38@@ -1,5 +1,6 @@
 39 #include "globals.hpp"
 40 #include "Hy3Layout.hpp"
 41+#include "SelectionHook.hpp"
 42 
 43 #include <src/Compositor.hpp>
 44 
 45@@ -197,6 +198,69 @@ void Hy3Node::recalcSizePosRecursive(bool force) {
 46 	}
 47 }
 48 
 49+void Hy3Node::markFocused() {
 50+	Hy3Node* node = this;
 51+
 52+	// undo decos for root focus
 53+	auto* root = node;
 54+	while (root->parent != nullptr) root = root->parent;
 55+	auto* oldfocus = root->getFocusedNode();
 56+
 57+	// update focus
 58+	if (this->data.type == Hy3NodeData::Group) {
 59+		this->data.as_group.lastFocusedChild = nullptr;
 60+	}
 61+
 62+	while (node->parent != nullptr) {
 63+		node->parent->data.as_group.lastFocusedChild = node;
 64+		node = node->parent;
 65+	}
 66+
 67+	if (oldfocus != nullptr) {
 68+		oldfocus->updateDecos();
 69+	}
 70+}
 71+
 72+void Hy3Node::focus() {
 73+	this->markFocused();
 74+
 75+	switch (this->data.type) {
 76+	case Hy3NodeData::Window:
 77+		g_pCompositor->focusWindow(this->data.as_window);
 78+		break;
 79+	case Hy3NodeData::Group:
 80+		g_pCompositor->focusWindow(nullptr);
 81+		this->raiseToTop();
 82+		break;
 83+	}
 84+}
 85+
 86+void Hy3Node::raiseToTop() {
 87+	switch (this->data.type) {
 88+	case Hy3NodeData::Window:
 89+		g_pCompositor->moveWindowToTop(this->data.as_window);
 90+		break;
 91+	case Hy3NodeData::Group:
 92+		for (auto* child: this->data.as_group.children) {
 93+			child->raiseToTop();
 94+		}
 95+		break;
 96+	}
 97+}
 98+
 99+Hy3Node* Hy3Node::getFocusedNode() {
100+	switch (this->data.type) {
101+	case Hy3NodeData::Window:
102+		return this;
103+	case Hy3NodeData::Group:
104+		if (this->data.as_group.lastFocusedChild == nullptr) {
105+			return this;
106+		} else {
107+			return this->data.as_group.lastFocusedChild->getFocusedNode();
108+		}
109+	}
110+}
111+
112 bool Hy3Node::swallowGroups(Hy3Node* into) {
113 	if (into == nullptr
114 			|| into->data.type != Hy3NodeData::Group
115@@ -291,6 +355,18 @@ void Hy3Node::swapData(Hy3Node& a, Hy3Node& b) {
116 	}
117 }
118 
119+void Hy3Node::updateDecos() {
120+	switch (this->data.type) {
121+	case Hy3NodeData::Window:
122+		g_pCompositor->updateWindowAnimatedDecorationValues(this->data.as_window);
123+		break;
124+	case Hy3NodeData::Group:
125+	  for (auto* child: this->data.as_group.children) {
126+			child->updateDecos();
127+		}
128+	}
129+}
130+
131 int Hy3Layout::getWorkspaceNodeCount(const int& id) {
132 	int count = 0;
133 
134@@ -496,7 +572,7 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window) {
135 	}
136 	Debug::log(LOG, "opened new window %p(node: %p) on window %p in %p", window, &node, opening_after, opening_into);
137 
138diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
139index a4d33843556e36629e2b5ebd0d1cac5393f6d520..fdeef7fb99d42598ea592921e43ab066bb13ceab 100644
140--- a/src/Hy3Layout.hpp
141+++ b/src/Hy3Layout.hpp
142@@ -71,6 +71,11 @@ struct Hy3Node {
143 
144 	void recalcSizePosRecursive(bool force = false);
145 	std::string debugNode();
146+	void markFocused();
147+	void focus();
148+	void raiseToTop();
149+	Hy3Node* getFocusedNode();
150+	void updateDecos();
151 
152 	bool operator==(const Hy3Node&) const;
153 
154@@ -105,9 +110,12 @@ public:
155 	virtual void onEnable();
156 	virtual void onDisable();
157 
158-	void makeGroupOn(CWindow*, Hy3GroupLayout);
159-	void shiftWindow(CWindow*, ShiftDirection);
160-	void shiftFocus(CWindow*, ShiftDirection);
161+	void makeGroupOn(int, Hy3GroupLayout);
162+	void shiftWindow(int, ShiftDirection);
163+	void shiftFocus(int, ShiftDirection);
164+	void raiseFocus(int);
165+
166+	bool shouldRenderSelected(CWindow*);
167 
168 	Hy3Node* getWorkspaceRootGroup(const int&);
169 
170diff --git a/src/SelectionHook.cpp b/src/SelectionHook.cpp
171new file mode 100644
172index 0000000000000000000000000000000000000000..c1b93b8b6fa3ec113d574661d94ea8190c160fc0
173--- /dev/null
174+++ b/src/SelectionHook.cpp
175@@ -0,0 +1,43 @@
176+#include "globals.hpp"
177+#include "src/Window.hpp"
178+#include <src/plugins/PluginAPI.hpp>
179+#include <src/Compositor.hpp>
180+
181+inline CFunctionHook* g_LastSelectionHook = nullptr;
182+
183+void hook_update_decos(void* thisptr, CWindow* window) {
184+	bool explicitly_selected = g_Hy3Layout->shouldRenderSelected(window);
185+	Debug::log(LOG, "update decos for %p - selected: %d", window, explicitly_selected);
186+
187+	auto* lastWindow = g_pCompositor->m_pLastWindow;
188+	if (explicitly_selected) {
189+		g_pCompositor->m_pLastWindow = window;
190+	}
191+
192+	((void (*)(void*, CWindow*)) g_LastSelectionHook->m_pOriginal)(thisptr, window);
193+
194+	if (explicitly_selected) {
195+		g_pCompositor->m_pLastWindow = lastWindow;
196+	}
197+}
198+
199+void setup_selection_hook() {
200+	if (g_LastSelectionHook == nullptr) {
201+		static const auto decoUpdateCandidates = HyprlandAPI::findFunctionsByName(PHANDLE, "updateWindowAnimatedDecorationValues");
202+
203+		if (decoUpdateCandidates.size() != 1) {
204+			Debug::log(ERR, "Expected one matching function to hook for \"updateWindowAnimatedDecorationValues\", found %d", decoUpdateCandidates.size());
205+			return;
206+		}
207+
208+		g_LastSelectionHook = HyprlandAPI::createFunctionHook(PHANDLE, decoUpdateCandidates[0].address, (void *)&hook_update_decos);
209+	}
210+
211+	g_LastSelectionHook->hook();
212+}
213+
214+void disable_selection_hook() {
215+	if (g_LastSelectionHook != nullptr) {
216+		g_LastSelectionHook->unhook();
217+	}
218+}
219diff --git a/src/SelectionHook.hpp b/src/SelectionHook.hpp
220new file mode 100644
221index 0000000000000000000000000000000000000000..f15fd2399f5f2f72e99195f5a68e0a3e447c1286
222--- /dev/null
223+++ b/src/SelectionHook.hpp
224@@ -0,0 +1,4 @@
225+#pragma once
226+
227+void setup_selection_hook();
228+void disable_selection_hook();
229diff --git a/src/globals.hpp b/src/globals.hpp
230index 8aac0ed85f6f543fb89425d7dbc2942ebfe0a5a2..6d165865e12cf390c89d2bbbac4a0bae05721a13 100644
231--- a/src/globals.hpp
232+++ b/src/globals.hpp
233@@ -1,3 +1,5 @@
234 #include <src/plugins/PluginAPI.hpp>
235+#include "Hy3Layout.hpp"
236 
237 inline HANDLE PHANDLE = nullptr;
238+inline std::unique_ptr<Hy3Layout> g_Hy3Layout;
239diff --git a/src/main.cpp b/src/main.cpp
240index 5c8aa0862afe157783d3bd8340cf6b76b3c5220b..2126376742558e2a396570ab0c4b71bbf0ade96e 100644
241--- a/src/main.cpp
242+++ b/src/main.cpp
243@@ -1,11 +1,8 @@
244 #include <src/plugins/PluginAPI.hpp>
245+#include <src/Compositor.hpp>
246 
247 #include "globals.hpp"
248-#include "Hy3Layout.hpp"
249-#include "src/Compositor.hpp"
250-#include "src/config/ConfigManager.hpp"
251 
252-inline std::unique_ptr<Hy3Layout> g_Hy3Layout;
253 
254 APICALL EXPORT std::string PLUGIN_API_VERSION() {
255 	return HYPRLAND_API_VERSION;
256@@ -25,53 +22,73 @@ CWindow* window_for_action() {
257 	return window;
258 }
259 
260+int workspace_for_action() {
261+	if (g_pLayoutManager->getCurrentLayout() != g_Hy3Layout.get()) return -1;
262+
263+	int workspace_id = g_pCompositor->m_pLastMonitor->activeWorkspace;
264+
265+	if (workspace_id < 0) return -1;
266+	auto* workspace = g_pCompositor->getWorkspaceByID(workspace_id);
267+	if (workspace == nullptr) return -1;
268+	if (workspace->m_bHasFullscreenWindow) return -1;
269+
270+	return workspace_id;
271+}
272+
273 void dispatch_makegroup(std::string arg) {
274-	CWindow* window = window_for_action();
275-	if (window == nullptr) return;
276+	int workspace = workspace_for_action();
277+	if (workspace < 0) return;
278 
279 	if (arg == "h") {
280-		g_Hy3Layout->makeGroupOn(window, Hy3GroupLayout::SplitH);
281+		g_Hy3Layout->makeGroupOn(workspace, Hy3GroupLayout::SplitH);
282 	} else if (arg == "v") {
283-		g_Hy3Layout->makeGroupOn(window, Hy3GroupLayout::SplitV);
284+		g_Hy3Layout->makeGroupOn(workspace, Hy3GroupLayout::SplitV);
285 	}
286 }
287 
288 void dispatch_movewindow(std::string arg) {
289-	CWindow* window = window_for_action();
290-	if (window == nullptr) return;
291+	int workspace = workspace_for_action();
292+	if (workspace < 0) return;
293 
294 	if (arg == "l") {
295-		g_Hy3Layout->shiftWindow(window, ShiftDirection::Left);
296+		g_Hy3Layout->shiftWindow(workspace, ShiftDirection::Left);
297 	} else if (arg == "u") {
298-		g_Hy3Layout->shiftWindow(window, ShiftDirection::Up);
299+		g_Hy3Layout->shiftWindow(workspace, ShiftDirection::Up);
300 	} else if (arg == "d") {
301-		g_Hy3Layout->shiftWindow(window, ShiftDirection::Down);
302+		g_Hy3Layout->shiftWindow(workspace, ShiftDirection::Down);
303 	} else if (arg == "r") {
304-		g_Hy3Layout->shiftWindow(window, ShiftDirection::Right);
305+		g_Hy3Layout->shiftWindow(workspace, ShiftDirection::Right);
306 	}
307 }
308 
309 void dispatch_movefocus(std::string arg) {
310-	CWindow* window = window_for_action();
311-	if (window == nullptr) return;
312+	int workspace = workspace_for_action();
313+	if (workspace < 0) return;
314 
315 	if (arg == "l") {
316-		g_Hy3Layout->shiftFocus(window, ShiftDirection::Left);
317+		g_Hy3Layout->shiftFocus(workspace, ShiftDirection::Left);
318 	} else if (arg == "u") {
319-		g_Hy3Layout->shiftFocus(window, ShiftDirection::Up);
320+		g_Hy3Layout->shiftFocus(workspace, ShiftDirection::Up);
321 	} else if (arg == "d") {
322-		g_Hy3Layout->shiftFocus(window, ShiftDirection::Down);
323+		g_Hy3Layout->shiftFocus(workspace, ShiftDirection::Down);
324 	} else if (arg == "r") {
325-		g_Hy3Layout->shiftFocus(window, ShiftDirection::Right);
326+		g_Hy3Layout->shiftFocus(workspace, ShiftDirection::Right);
327 	}
328 }
329 
330+void dispatch_raisefocus(std::string arg) {
331+	int workspace = workspace_for_action();
332+	if (workspace < 0) return;
333+
334+	g_Hy3Layout->raiseFocus(workspace);
335+}
336+
337 void dispatch_debug(std::string arg) {
338-	CWindow* window = window_for_action();
339-	if (window == nullptr) return;
340+	int workspace = workspace_for_action();
341+	if (workspace < 0) return;
342