b171721e66f37d2474fd44b49d03055cd57689fa

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

Message

Initial commit

Some progress already but have to commit somewhere

Diff

This diff is truncated to protect this page.

  1diff --git a/.gitignore b/.gitignore
  2new file mode 100644
  3index 0000000000000000000000000000000000000000..b159eae01cad89d899b5e90e99e0bd98d6b0d51c
  4--- /dev/null
  5+++ b/.gitignore
  6@@ -0,0 +1,2 @@
  7+build/
  8+compile_commands.json
  9diff --git a/CMakeLists.txt b/CMakeLists.txt
 10new file mode 100644
 11index 0000000000000000000000000000000000000000..89ce72b1c7d8db197fdc980fa928ec034a0e11f7
 12--- /dev/null
 13+++ b/CMakeLists.txt
 14@@ -0,0 +1,29 @@
 15+cmake_minimum_required(VERSION 3.19)
 16+project(Hy3 VERSION "0.1")
 17+set(CMAKE_CXX_STANDARD 23)
 18+add_compile_definitions(WLR_USE_UNSTABLE)
 19+add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith)
 20+
 21+# nix workaround
 22+if(CMAKE_EXPORT_COMPILE_COMMANDS)
 23+  set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES
 24+      ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
 25+endif()
 26+
 27+IF(NOT DEFINED ENV{HYPRLAND_HEADERS})
 28+ message(FATAL_ERROR "$HYPRLAND_HEADERS is unset")
 29+ENDIF()
 30+
 31+find_package(PkgConfig REQUIRED)
 32+pkg_check_modules(DEPS REQUIRED pixman-1 libdrm)
 33+
 34+add_library(hy3 SHARED
 35+	src/main.cpp
 36+	src/Hy3Layout.cpp
 37+	src/Hy3SpawnLocDecoration.cpp
 38+)
 39+
 40+target_include_directories(hy3 PRIVATE
 41+	${DEPS_INCLUDE_DIRS}
 42+	$ENV{HYPRLAND_HEADERS}
 43+)
 44diff --git a/compile_commands_bear.sh b/compile_commands_bear.sh
 45new file mode 100755
 46index 0000000000000000000000000000000000000000..f4a6ace9184b5f4d33c8c0962d9cfba614483ae4
 47--- /dev/null
 48+++ b/compile_commands_bear.sh
 49@@ -0,0 +1,8 @@
 50+rm -rf build
 51+mkdir -p build
 52+cd build
 53+cmake ..
 54+bear -- make
 55+mv compile_commands.json ..
 56+cd ..
 57+sed -i 's/-std=gnu++23/-std=gnu++2b/g' compile_commands.json
 58diff --git a/compile_commands_cmake.sh b/compile_commands_cmake.sh
 59new file mode 100755
 60index 0000000000000000000000000000000000000000..ddf12433992b9ec75bf4476d8e7ca5c8a34adfe2
 61--- /dev/null
 62+++ b/compile_commands_cmake.sh
 63@@ -0,0 +1,6 @@
 64+mkdir -p build
 65+cd build
 66+cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ..
 67+mv compile_commands.json ..
 68+cd ..
 69+sed -i 's/-std=gnu++23/-std=gnu++2b/g' compile_commands.json
 70diff --git a/flake.lock b/flake.lock
 71new file mode 100644
 72index 0000000000000000000000000000000000000000..eaf318df05f911cf35b3f049bc0532c1a3896666
 73--- /dev/null
 74+++ b/flake.lock
 75@@ -0,0 +1,132 @@
 76+{
 77+  "nodes": {
 78+    "flake-utils": {
 79+      "locked": {
 80+        "lastModified": 1678901627,
 81+        "narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=",
 82+        "owner": "numtide",
 83+        "repo": "flake-utils",
 84+        "rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6",
 85+        "type": "github"
 86+      },
 87+      "original": {
 88+        "owner": "numtide",
 89+        "repo": "flake-utils",
 90+        "type": "github"
 91+      }
 92+    },
 93+    "hyprland": {
 94+      "inputs": {
 95+        "hyprland-protocols": "hyprland-protocols",
 96+        "nixpkgs": "nixpkgs",
 97+        "wlroots": "wlroots",
 98+        "xdph": "xdph"
 99+      },
100+      "locked": {
101+        "lastModified": 1680645538,
102+        "narHash": "sha256-H9gAx2U3XKVoI2WjwbOXULL4TYj9y7ctxBB4up0v9tI=",
103+        "owner": "hyprwm",
104+        "repo": "Hyprland",
105+        "rev": "a80ba54bbc6a423ab0e79730442e09aea832d9d6",
106+        "type": "github"
107+      },
108+      "original": {
109+        "owner": "hyprwm",
110+        "repo": "Hyprland",
111+        "type": "github"
112+      }
113+    },
114+    "hyprland-protocols": {
115+      "inputs": {
116+        "nixpkgs": [
117+          "hyprland",
118+          "nixpkgs"
119+        ]
120+      },
121+      "locked": {
122+        "lastModified": 1671839510,
123+        "narHash": "sha256-+PY1qqJfmZzzROgcIY4I7AkCwpnC+qBIYk2eFoA9RWc=",
124+        "owner": "hyprwm",
125+        "repo": "hyprland-protocols",
126+        "rev": "b8f55e02a328c47ed373133c52483bbfa20a1b75",
127+        "type": "github"
128+      },
129+      "original": {
130+        "owner": "hyprwm",
131+        "repo": "hyprland-protocols",
132+        "type": "github"
133+      }
134+    },
135+    "nixpkgs": {
136+      "locked": {
137+        "lastModified": 1680487167,
138+        "narHash": "sha256-9FNIqrxDZgSliGGN2XJJSvcDYmQbgOANaZA4UWnTdg4=",
139+        "owner": "NixOS",
140+        "repo": "nixpkgs",
141+        "rev": "53dad94e874c9586e71decf82d972dfb640ef044",
142+        "type": "github"
143+      },
144+      "original": {
145+        "owner": "NixOS",
146+        "ref": "nixos-unstable",
147+        "repo": "nixpkgs",
148+        "type": "github"
149+      }
150+    },
151+    "root": {
152+      "inputs": {
153+        "flake-utils": "flake-utils",
154+        "hyprland": "hyprland",
155+        "nixpkgs": [
156+          "hyprland",
157+          "nixpkgs"
158+        ]
159+      }
160+    },
161+    "wlroots": {
162+      "flake": false,
163+      "locked": {
164+        "host": "gitlab.freedesktop.org",
165+        "lastModified": 1680629978,
166+        "narHash": "sha256-2iVx5zqU2CpMgmtVadsHSkhkAsoxAWKQp6RQqt2OgQY=",
167+        "owner": "wlroots",
168+        "repo": "wlroots",
169+        "rev": "835208db98a29431fa687c9506f4b43fe645ff65",
170+        "type": "gitlab"
171+      },
172+      "original": {
173+        "host": "gitlab.freedesktop.org",
174+        "owner": "wlroots",
175diff --git a/flake.nix b/flake.nix
176new file mode 100644
177index 0000000000000000000000000000000000000000..376bb8a629b9569fb084cc9a21cc75504cf3d6fe
178--- /dev/null
179+++ b/flake.nix
180@@ -0,0 +1,51 @@
181+{
182+  inputs = {
183+    hyprland.url = "github:hyprwm/Hyprland";
184+    nixpkgs.follows = "hyprland/nixpkgs";
185+    flake-utils.url = "github:numtide/flake-utils";
186+  };
187+
188+  outputs = { nixpkgs, hyprland, flake-utils, ... }:
189+    flake-utils.lib.eachDefaultSystem (system: let
190+      pkgs = import nixpkgs { inherit system; };
191+      hyprpkgs = hyprland.packages.${system};
192+    in {
193+      packages.default = pkgs.gcc12Stdenv.mkDerivation {
194+        pname = "hy3";
195+        version = "0.1";
196+
197+        src = ./.;
198+
199+        nativeBuildInputs = with pkgs; [
200+          cmake
201+          pkg-config
202+        ];
203+
204+        #HYPRLAND_HEADERS = hyprpkgs.hyprland.src; - TODO
205+      };
206+
207+      devShells.default = pkgs.mkShell.override { stdenv = pkgs.gcc12Stdenv; } {
208+        name = "hy3-shell";
209+        nativeBuildInputs = with pkgs; [
210+          cmake
211+          pkg-config
212+
213+          clang-tools_15
214+          bear
215+        ];
216+
217+        buildInputs = with pkgs; [
218+          hyprpkgs.wlroots-hyprland
219+          libdrm
220+          pixman
221+        ];
222+
223+        inputsFrom = [
224+          hyprpkgs.hyprland
225+          hyprpkgs.wlroots-hyprland
226+        ];
227+
228+        #HYPRLAND_HEADERS = hyprpkgs.hyprland.src; - TODO
229+      };
230+    });
231+}
232diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
233new file mode 100644
234index 0000000000000000000000000000000000000000..339496db1c3285e3662b42f5be6931bf75f0018e
235--- /dev/null
236+++ b/src/Hy3Layout.cpp
237@@ -0,0 +1,452 @@
238+#include "Hy3Layout.hpp"
239+#include "src/Window.hpp"
240+#include "src/debug/Log.hpp"
241+#include "src/helpers/Vector2D.hpp"
242+#include "src/helpers/Workspace.hpp"
243+#include "src/managers/XWaylandManager.hpp"
244+#include "src/managers/input/InputManager.hpp"
245+#include "src/render/Renderer.hpp"
246+#include <cairo/cairo.h>
247+#include <ctime>
248+#include <memory>
249+#include <src/Compositor.hpp>
250+
251+Hy3GroupData::Hy3GroupData(Hy3GroupLayout layout): layout(layout) {}
252+
253+Hy3NodeData::Hy3NodeData(): Hy3NodeData((CWindow*)nullptr) {}
254+
255+Hy3NodeData::Hy3NodeData(CWindow *window): type(Hy3NodeData::Window) {
256+	this->as_window = window;
257+}
258+
259+Hy3NodeData::Hy3NodeData(Hy3GroupData group): type(Hy3NodeData::Group) {
260+	new(&this->as_group) Hy3GroupData(std::move(group));
261+}
262+
263+Hy3NodeData::~Hy3NodeData() {
264+	switch (this->type) {
265+	case Hy3NodeData::Window:
266+		break;
267+	case Hy3NodeData::Group:
268+		this->as_group.~Hy3GroupData();
269+
270+		// who ever thought calling the dtor after a move was a good idea?
271+		this->type = Hy3NodeData::Window;
272+		break;
273+	}
274+}
275+
276+Hy3NodeData::Hy3NodeData(const Hy3NodeData& from): type(from.type) {
277+	Debug::log(LOG, "Copy CTor type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group);
278+	switch (from.type) {
279+	case Hy3NodeData::Window:
280+		this->as_window = from.as_window;
281+		break;
282+	case Hy3NodeData::Group:
283+		new(&this->as_group) Hy3GroupData(from.as_group);
284+		break;
285+	}
286+}
287+
288+Hy3NodeData::Hy3NodeData(Hy3NodeData&& from): type(from.type) {
289+	Debug::log(LOG, "Move CTor type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group);
290+	switch (from.type) {
291+	case Hy3NodeData::Window:
292+		this->as_window = from.as_window;
293+		break;
294+	case Hy3NodeData::Group:
295+		new(&this->as_group) Hy3GroupData(std::move(from.as_group));
296+		break;
297+	}
298+}
299+
300+Hy3NodeData& Hy3NodeData::operator=(const Hy3NodeData& from) {
301+	Debug::log(LOG, "operator= type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group);
302+	if (this->type == Hy3NodeData::Group) {
303+		this->as_group.~Hy3GroupData();
304+	}
305+
306+	this->type = from.type;
307+
308+	switch (this->type) {
309+	case Hy3NodeData::Window:
310+		this->as_window = from.as_window;
311+		break;
312+	case Hy3NodeData::Group:
313+		new(&this->as_group) Hy3GroupData(from.as_group);
314+		break;
315+	}
316+
317+	return *this;
318+}
319+
320+bool Hy3NodeData::operator==(const Hy3NodeData& rhs) const {
321+	if (this->type != rhs.type) return false;
322+	switch (this->type) {
323+	case Hy3NodeData::Window:
324+		return this->as_window == rhs.as_window;
325+	case Hy3NodeData::Group:
326+		return this->as_group.children == rhs.as_group.children;
327+	}
328+
329+	return false;
330+}
331+
332+bool Hy3Node::operator==(const Hy3Node& rhs) const {
333+	return this->data == rhs.data;
334+}
335+
336+void Hy3Node::recalcSizePosRecursive(bool force) {
337diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
338new file mode 100644
339index 0000000000000000000000000000000000000000..7d23e8054abadd2b20e316f515cb5553f7eddfb5
340--- /dev/null
341+++ b/src/Hy3Layout.hpp
342@@ -0,0 +1,85 @@
343+#pragma once
344+
345+#include <list>
346+#include <src/layout/IHyprLayout.hpp>
347+
348+class Hy3Layout;
349+struct Hy3Node;
350+
351+enum class Hy3GroupLayout {
352+	SplitH,
353+	SplitV,
354+	Tabbed,
355+};
356+
357+struct Hy3GroupData {
358+	Hy3GroupLayout layout = Hy3GroupLayout::SplitH;
359+	std::list<Hy3Node*> children;
360+
361+	Hy3GroupData(Hy3GroupLayout layout);
362+};
363+
364+struct Hy3NodeData {
365+	enum { Group, Window } type;
366+	union {
367+		Hy3GroupData as_group;
368+		CWindow* as_window;
369+	};
370+
371+	bool operator==(const Hy3NodeData&) const;
372+
373+	Hy3NodeData();
374+	Hy3NodeData(CWindow* window);
375+	Hy3NodeData(Hy3GroupData group);
376+	Hy3NodeData(const Hy3NodeData&);
377+	Hy3NodeData(Hy3NodeData&&);
378+	Hy3NodeData& operator=(const Hy3NodeData&);
379+	~Hy3NodeData();
380+};
381+
382+struct Hy3Node {
383+	Hy3Node* parent = nullptr;
384+	Hy3NodeData data;
385+	Vector2D position;
386+	Vector2D size;
387+	float size_ratio = 1.0;
388+	int workspace_id = -1;
389+	bool valid = true;
390+	Hy3Layout* layout = nullptr;
391+
392+	void recalcSizePosRecursive(bool force = false);
393+
394+	bool operator==(const Hy3Node&) const;
395+};
396+
397+class Hy3Layout: public IHyprLayout {
398+public:
399+	virtual void onWindowCreatedTiling(CWindow*);
400+	virtual void onWindowRemovedTiling(CWindow*);
401+	virtual bool isWindowTiled(CWindow*);
402+	virtual void recalculateMonitor(const int&);
403+	virtual void recalculateWindow(CWindow*);
404+	virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr);
405+	virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool);
406+	virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
407+	virtual SWindowRenderLayoutHints requestRenderHints(CWindow*);
408+	virtual void switchWindows(CWindow*, CWindow*);
409+	virtual void alterSplitRatio(CWindow*, float, bool);
410+	virtual std::string getLayoutName();
411+	virtual void replaceWindowDataWith(CWindow* from, CWindow* to);
412+
413+	virtual void onEnable();
414+	virtual void onDisable();
415+
416+private:
417+	// std::list is used over std::vector because it does not invalidate references
418+	// when mutated.
419+	std::list<Hy3Node> nodes;
420+
421+	int getWorkspaceNodeCount(const int&);
422+	Hy3Node* getNodeFromWindow(CWindow*);
423+	Hy3Node* getWorkspaceRootGroup(const int&);
424+	void applyNodeDataToWindow(Hy3Node*, bool force = false);
425+
426+	friend struct Hy3Node;
427+};
428diff --git a/src/main.cpp b/src/main.cpp
429new file mode 100644
430index 0000000000000000000000000000000000000000..350e61ca9c8999d0fc9d305a1b87ad2fb1ba13ab
431--- /dev/null
432+++ b/src/main.cpp
433@@ -0,0 +1,49 @@
434+#include <src/plugins/PluginAPI.hpp>
435+
436+#include "Hy3Layout.hpp"
437+#include "src/Compositor.hpp"
438+
439+inline HANDLE PHANDLE = nullptr;
440+inline std::unique_ptr<Hy3Layout> g_Hy3Layout;
441+
442+APICALL EXPORT std::string PLUGIN_API_VERSION() {
443+	return HYPRLAND_API_VERSION;
444+}
445+
446+void splith(std::string) {
447+	SLayoutMessageHeader header;
448+	header.pWindow = g_pCompositor->m_pLastWindow;
449+
450+	if (!header.pWindow) return;
451+
452+	const auto workspace = g_pCompositor->getWorkspaceByID(header.pWindow->m_iWorkspaceID);
453+	if (workspace->m_bHasFullscreenWindow) return;
454+
455+	g_pLayoutManager->getCurrentLayout()->layoutMessage(header, "splith");
456+}
457+
458+void splitv(std::string) {
459+	SLayoutMessageHeader header;
460+	header.pWindow = g_pCompositor->m_pLastWindow;
461+
462+	if (!header.pWindow) return;
463+
464+	const auto workspace = g_pCompositor->getWorkspaceByID(header.pWindow->m_iWorkspaceID);
465+	if (workspace->m_bHasFullscreenWindow) return;
466+
467+	g_pLayoutManager->getCurrentLayout()->layoutMessage(header, "splitv");
468+}
469+
470+APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
471+	PHANDLE = handle;
472+
473+	g_Hy3Layout = std::make_unique<Hy3Layout>();
474+	HyprlandAPI::addLayout(PHANDLE, "hy3", g_Hy3Layout.get());
475+
476+	HyprlandAPI::addDispatcher(PHANDLE, "splith", splith);
477+	HyprlandAPI::addDispatcher(PHANDLE, "splitv", splitv);
478+
479+	return {"hy3", "i3 like layout for hyprland", "outfoxxed", "0.1"};
480+}
481+
482+APICALL EXPORT void PLUGIN_EXIT() {}