9b570f066a1497460dbb04ccb3dbebd833fda78b
- Author
- outfoxxed <outfoxxed@outfoxxed.me>
- Committer
- outfoxxed <outfoxxed@outfoxxed.me>
- Date
Message
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