From b171721e66f37d2474fd44b49d03055cd57689fa Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Wed, 12 Apr 2023 01:33:00 -0700 Subject: [PATCH] Initial commit Some progress already but have to commit somewhere --- .gitignore | 2 + CMakeLists.txt | 29 +++ compile_commands_bear.sh | 8 + compile_commands_cmake.sh | 6 + flake.lock | 132 +++++++++++ flake.nix | 51 +++++ src/Hy3Layout.cpp | 452 ++++++++++++++++++++++++++++++++++++++ src/Hy3Layout.hpp | 85 +++++++ src/main.cpp | 49 +++++ 9 files changed, 814 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100755 compile_commands_bear.sh create mode 100755 compile_commands_cmake.sh create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/Hy3Layout.cpp create mode 100644 src/Hy3Layout.hpp create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b159eae --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +compile_commands.json \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..89ce72b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.19) +project(Hy3 VERSION "0.1") +set(CMAKE_CXX_STANDARD 23) +add_compile_definitions(WLR_USE_UNSTABLE) +add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith) + +# nix workaround +if(CMAKE_EXPORT_COMPILE_COMMANDS) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES + ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +endif() + +IF(NOT DEFINED ENV{HYPRLAND_HEADERS}) + message(FATAL_ERROR "$HYPRLAND_HEADERS is unset") +ENDIF() + +find_package(PkgConfig REQUIRED) +pkg_check_modules(DEPS REQUIRED pixman-1 libdrm) + +add_library(hy3 SHARED + src/main.cpp + src/Hy3Layout.cpp + src/Hy3SpawnLocDecoration.cpp +) + +target_include_directories(hy3 PRIVATE + ${DEPS_INCLUDE_DIRS} + $ENV{HYPRLAND_HEADERS} +) diff --git a/compile_commands_bear.sh b/compile_commands_bear.sh new file mode 100755 index 0000000..f4a6ace --- /dev/null +++ b/compile_commands_bear.sh @@ -0,0 +1,8 @@ +rm -rf build +mkdir -p build +cd build +cmake .. +bear -- make +mv compile_commands.json .. +cd .. +sed -i 's/-std=gnu++23/-std=gnu++2b/g' compile_commands.json diff --git a/compile_commands_cmake.sh b/compile_commands_cmake.sh new file mode 100755 index 0000000..ddf1243 --- /dev/null +++ b/compile_commands_cmake.sh @@ -0,0 +1,6 @@ +mkdir -p build +cd build +cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .. +mv compile_commands.json .. +cd .. +sed -i 's/-std=gnu++23/-std=gnu++2b/g' compile_commands.json diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..eaf318d --- /dev/null +++ b/flake.lock @@ -0,0 +1,132 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1678901627, + "narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "hyprland": { + "inputs": { + "hyprland-protocols": "hyprland-protocols", + "nixpkgs": "nixpkgs", + "wlroots": "wlroots", + "xdph": "xdph" + }, + "locked": { + "lastModified": 1680645538, + "narHash": "sha256-H9gAx2U3XKVoI2WjwbOXULL4TYj9y7ctxBB4up0v9tI=", + "owner": "hyprwm", + "repo": "Hyprland", + "rev": "a80ba54bbc6a423ab0e79730442e09aea832d9d6", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "Hyprland", + "type": "github" + } + }, + "hyprland-protocols": { + "inputs": { + "nixpkgs": [ + "hyprland", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1671839510, + "narHash": "sha256-+PY1qqJfmZzzROgcIY4I7AkCwpnC+qBIYk2eFoA9RWc=", + "owner": "hyprwm", + "repo": "hyprland-protocols", + "rev": "b8f55e02a328c47ed373133c52483bbfa20a1b75", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprland-protocols", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1680487167, + "narHash": "sha256-9FNIqrxDZgSliGGN2XJJSvcDYmQbgOANaZA4UWnTdg4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "53dad94e874c9586e71decf82d972dfb640ef044", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "hyprland": "hyprland", + "nixpkgs": [ + "hyprland", + "nixpkgs" + ] + } + }, + "wlroots": { + "flake": false, + "locked": { + "host": "gitlab.freedesktop.org", + "lastModified": 1680629978, + "narHash": "sha256-2iVx5zqU2CpMgmtVadsHSkhkAsoxAWKQp6RQqt2OgQY=", + "owner": "wlroots", + "repo": "wlroots", + "rev": "835208db98a29431fa687c9506f4b43fe645ff65", + "type": "gitlab" + }, + "original": { + "host": "gitlab.freedesktop.org", + "owner": "wlroots", + "repo": "wlroots", + "type": "gitlab" + } + }, + "xdph": { + "inputs": { + "hyprland-protocols": [ + "hyprland", + "hyprland-protocols" + ], + "nixpkgs": [ + "hyprland", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1673116118, + "narHash": "sha256-eR0yDSkR2XYMesfdRWJs25kAdXET2mbNNHu5t+KUcKA=", + "owner": "hyprwm", + "repo": "xdg-desktop-portal-hyprland", + "rev": "d479c846531fd0e1d2357c9588b8310a2b859ef2", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "xdg-desktop-portal-hyprland", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..376bb8a --- /dev/null +++ b/flake.nix @@ -0,0 +1,51 @@ +{ + inputs = { + hyprland.url = "github:hyprwm/Hyprland"; + nixpkgs.follows = "hyprland/nixpkgs"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { nixpkgs, hyprland, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { inherit system; }; + hyprpkgs = hyprland.packages.${system}; + in { + packages.default = pkgs.gcc12Stdenv.mkDerivation { + pname = "hy3"; + version = "0.1"; + + src = ./.; + + nativeBuildInputs = with pkgs; [ + cmake + pkg-config + ]; + + #HYPRLAND_HEADERS = hyprpkgs.hyprland.src; - TODO + }; + + devShells.default = pkgs.mkShell.override { stdenv = pkgs.gcc12Stdenv; } { + name = "hy3-shell"; + nativeBuildInputs = with pkgs; [ + cmake + pkg-config + + clang-tools_15 + bear + ]; + + buildInputs = with pkgs; [ + hyprpkgs.wlroots-hyprland + libdrm + pixman + ]; + + inputsFrom = [ + hyprpkgs.hyprland + hyprpkgs.wlroots-hyprland + ]; + + #HYPRLAND_HEADERS = hyprpkgs.hyprland.src; - TODO + }; + }); +} diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp new file mode 100644 index 0000000..339496d --- /dev/null +++ b/src/Hy3Layout.cpp @@ -0,0 +1,452 @@ +#include "Hy3Layout.hpp" +#include "src/Window.hpp" +#include "src/debug/Log.hpp" +#include "src/helpers/Vector2D.hpp" +#include "src/helpers/Workspace.hpp" +#include "src/managers/XWaylandManager.hpp" +#include "src/managers/input/InputManager.hpp" +#include "src/render/Renderer.hpp" +#include +#include +#include +#include + +Hy3GroupData::Hy3GroupData(Hy3GroupLayout layout): layout(layout) {} + +Hy3NodeData::Hy3NodeData(): Hy3NodeData((CWindow*)nullptr) {} + +Hy3NodeData::Hy3NodeData(CWindow *window): type(Hy3NodeData::Window) { + this->as_window = window; +} + +Hy3NodeData::Hy3NodeData(Hy3GroupData group): type(Hy3NodeData::Group) { + new(&this->as_group) Hy3GroupData(std::move(group)); +} + +Hy3NodeData::~Hy3NodeData() { + switch (this->type) { + case Hy3NodeData::Window: + break; + case Hy3NodeData::Group: + this->as_group.~Hy3GroupData(); + + // who ever thought calling the dtor after a move was a good idea? + this->type = Hy3NodeData::Window; + break; + } +} + +Hy3NodeData::Hy3NodeData(const Hy3NodeData& from): type(from.type) { + Debug::log(LOG, "Copy CTor type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group); + switch (from.type) { + case Hy3NodeData::Window: + this->as_window = from.as_window; + break; + case Hy3NodeData::Group: + new(&this->as_group) Hy3GroupData(from.as_group); + break; + } +} + +Hy3NodeData::Hy3NodeData(Hy3NodeData&& from): type(from.type) { + Debug::log(LOG, "Move CTor type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group); + switch (from.type) { + case Hy3NodeData::Window: + this->as_window = from.as_window; + break; + case Hy3NodeData::Group: + new(&this->as_group) Hy3GroupData(std::move(from.as_group)); + break; + } +} + +Hy3NodeData& Hy3NodeData::operator=(const Hy3NodeData& from) { + Debug::log(LOG, "operator= type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group); + if (this->type == Hy3NodeData::Group) { + this->as_group.~Hy3GroupData(); + } + + this->type = from.type; + + switch (this->type) { + case Hy3NodeData::Window: + this->as_window = from.as_window; + break; + case Hy3NodeData::Group: + new(&this->as_group) Hy3GroupData(from.as_group); + break; + } + + return *this; +} + +bool Hy3NodeData::operator==(const Hy3NodeData& rhs) const { + if (this->type != rhs.type) return false; + switch (this->type) { + case Hy3NodeData::Window: + return this->as_window == rhs.as_window; + case Hy3NodeData::Group: + return this->as_group.children == rhs.as_group.children; + } + + return false; +} + +bool Hy3Node::operator==(const Hy3Node& rhs) const { + return this->data == rhs.data; +} + +void Hy3Node::recalcSizePosRecursive(bool force) { + if (this->data.type != Hy3NodeData::Group) { + this->layout->applyNodeDataToWindow(this, force); + return; + } + + auto* group = &this->data.as_group; + int constraint; + switch (group->layout) { + case Hy3GroupLayout::SplitH: + constraint = this->size.x; + break; + case Hy3GroupLayout::SplitV: + constraint = this->size.y; + break; + case Hy3GroupLayout::Tabbed: + break; + } + + double offset = 0; + + for(auto child: group->children) { + switch (group->layout) { + case Hy3GroupLayout::SplitH: + child->position.x = this->position.x + offset; + child->size.x = child->size_ratio * ((double) constraint / group->children.size()); + offset += child->size.x; + child->position.y = this->position.y; + child->size.y = this->size.y; + break; + case Hy3GroupLayout::SplitV: + child->position.y = this->position.y + offset; + child->size.y = child->size_ratio * ((double) constraint / group->children.size()); + offset += child->size.y; + child->position.x = this->position.x; + child->size.x = this->size.x; + break; + case Hy3GroupLayout::Tabbed: + // TODO: tab bars + child->position = this->position; + child->size = this->size; + break; + } + + child->recalcSizePosRecursive(force); + } +} + +int Hy3Layout::getWorkspaceNodeCount(const int& id) { + int count = 0; + + for (auto& node: this->nodes) { + if (node.workspace_id == id && node.valid) count++; + } + + return count; +} + +Hy3Node* Hy3Layout::getNodeFromWindow(CWindow* window) { + for (auto& node: this->nodes) { + if (node.data.type == Hy3NodeData::Window && node.data.as_window == window) { + return &node; + } + } + + return nullptr; +} + +Hy3Node* Hy3Layout::getWorkspaceRootGroup(const int& id) { + for (auto& node: this->nodes) { + if (node.parent == nullptr && node.data.type == Hy3NodeData::Group) { + return &node; + } + } + + return nullptr; +} + +void Hy3Layout::applyNodeDataToWindow(Hy3Node* node, bool force) { + if (node->data.type != Hy3NodeData::Window) return; + CWindow* window = node->data.as_window; + + CMonitor* monitor = nullptr; + + if (g_pCompositor->isWorkspaceSpecial(node->workspace_id)) { + for (auto& m: g_pCompositor->m_vMonitors) { + if (m->specialWorkspaceID == node->workspace_id) { + monitor = m.get(); + break; + } + } + } else { + monitor = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(node->workspace_id)->m_iMonitorID); + } + + if (monitor == nullptr) { + Debug::log(ERR, "Orphaned Node %x (workspace ID: %i)!!", node, node->workspace_id); + return; + } + + // for gaps outer + const bool display_left = STICKS(node->position.x, monitor->vecPosition.x + monitor->vecReservedTopLeft.x); + const bool display_right = STICKS(node->position.x + node->size.x, monitor->vecPosition.x + monitor->vecSize.x - monitor->vecReservedBottomRight.x); + const bool display_top = STICKS(node->position.y, monitor->vecPosition.y + monitor->vecReservedTopLeft.y); + const bool display_bottom = STICKS(node->position.y + node->size.y, monitor->vecPosition.y + monitor->vecSize.y - monitor->vecReservedBottomRight.y); + + const auto* border_size = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue; + const auto* gaps_in = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue; + const auto* gaps_out = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue; + static auto* const single_window_no_gaps = &g_pConfigManager->getConfigValuePtr("plugin:hy3:no_gaps_when_only")->intValue; + + if (!g_pCompositor->windowExists(window) || !window->m_bIsMapped) { + Debug::log(ERR, "Node %p holding invalid window %p!!", node, window); + this->onWindowRemovedTiling(window); + return; + } + + window->m_vSize = node->size; + window->m_vPosition = node->position; + + auto calcPos = window->m_vPosition + Vector2D(*border_size, *border_size); + auto calcSize = window->m_vSize - Vector2D(2 * *border_size, 2 * *border_size); + + const auto workspace_node_count = this->getWorkspaceNodeCount(window->m_iWorkspaceID); + + if (*single_window_no_gaps + && !g_pCompositor->isWorkspaceSpecial(window->m_iWorkspaceID) + && (workspace_node_count == 1 + || (window->m_bIsFullscreen + && g_pCompositor->getWorkspaceByID(window->m_iWorkspaceID)->m_efFullscreenMode == FULLSCREEN_MAXIMIZED))) + { + window->m_vRealPosition = window->m_vPosition; + window->m_vRealSize = window->m_vSize; + + window->updateWindowDecos(); + + window->m_sSpecialRenderData.rounding = false; + window->m_sSpecialRenderData.border = false; + window->m_sSpecialRenderData.decorate = false; + } else { + window->m_sSpecialRenderData.rounding = true; + window->m_sSpecialRenderData.border = true; + window->m_sSpecialRenderData.decorate = true; + + Vector2D offset_topleft( + display_left ? *gaps_out : *gaps_in, + display_top ? *gaps_out : *gaps_in + ); + + Vector2D offset_bottomright( + display_right ? *gaps_out : *gaps_in, + display_bottom ? *gaps_out : *gaps_in + ); + + calcPos = calcPos + offset_topleft; + calcSize = calcSize - offset_topleft - offset_bottomright; + + const auto reserved_area = window->getFullWindowReservedArea(); + calcPos = calcPos + reserved_area.topLeft; + calcSize = calcSize - (reserved_area.topLeft - reserved_area.bottomRight); + + window->m_vRealPosition = calcPos; + window->m_vRealSize = calcSize; + Debug::log(LOG, "Set size (%f %f)", calcSize.x, calcSize.y); + + g_pXWaylandManager->setWindowSize(window, calcSize); + + if (force) { + g_pHyprRenderer->damageWindow(window); + + window->m_vRealPosition.warp(); + window->m_vRealPosition.warp(); + + g_pHyprRenderer->damageWindow(window); + } + + window->updateWindowDecos(); + } +} + +void Hy3Layout::onWindowCreatedTiling(CWindow* window) { + if (window->m_bIsFloating) return; + + auto* monitor = g_pCompositor->getMonitorFromID(window->m_iMonitorID); + + Hy3Node* opening_into; + Hy3Node* opening_after; + + if (g_pCompositor->m_pLastWindow != nullptr + && !g_pCompositor->m_pLastWindow->m_bIsFloating + && g_pCompositor->m_pLastWindow != window + && g_pCompositor->m_pLastWindow->m_iWorkspaceID == window->m_iWorkspaceID + && g_pCompositor->m_pLastWindow->m_bIsMapped) + { + opening_after = this->getNodeFromWindow(g_pCompositor->m_pLastWindow); + } else { + opening_after = this->getNodeFromWindow(g_pCompositor->vectorToWindowTiled(g_pInputManager->getMouseCoordsInternal())); + } + + if (opening_after != nullptr) { + opening_into = opening_after->parent; + } else { + if ((opening_into = this->getWorkspaceRootGroup(window->m_iWorkspaceID)) == nullptr) { + this->nodes.push_back({ + .data = Hy3NodeData(Hy3GroupData(Hy3GroupLayout::SplitH)), + .position = monitor->vecPosition, + .size = monitor->vecSize, + .workspace_id = window->m_iWorkspaceID, + .layout = this, + }); + + opening_into = &this->nodes.back(); + } + } + + if (opening_into->data.type != Hy3NodeData::Group) { + Debug::log(ERR, "opening_into node %p was not of type Group", opening_into); + return; + } + + this->nodes.push_back({ + .parent = opening_into, + .data = Hy3NodeData(window), + .workspace_id = window->m_iWorkspaceID, + .layout = this, + }); + + auto& node = this->nodes.back(); + + if (opening_after == nullptr) { + opening_into->data.as_group.children.push_back(&node); + } else { + auto& children = opening_into->data.as_group.children; + auto iter = std::find(children.begin(), children.end(), opening_after); + auto iter2 = std::next(iter); + children.insert(iter2, &node); + } + Debug::log(LOG, "open new window %p(node: %p:%p) on winodow %p in %p", window, &node, node.data.as_window, opening_after, opening_into); + + opening_into->recalcSizePosRecursive(); +} + +void Hy3Layout::onWindowRemovedTiling(CWindow* window) { + auto* node = this->getNodeFromWindow(window); + Debug::log(LOG, "remove tiling %p (window %p)", node, window); + + if (node == nullptr) { + Debug::log(ERR, "onWindowRemovedTiling node null?"); + return; + } + + window->m_sSpecialRenderData.rounding = true; + window->m_sSpecialRenderData.border = true; + window->m_sSpecialRenderData.decorate = true; + + if (window->m_bIsFullscreen) { + g_pCompositor->setWindowFullscreen(window, false, FULLSCREEN_FULL); + } + + auto* parent = node->parent; + + parent->data.as_group.children.remove(node); + this->nodes.remove(*node); + + while (parent != nullptr && parent->data.as_group.children.empty()) { + auto* child = parent; + parent = parent->parent; + + if (parent != nullptr) parent->data.as_group.children.remove(child); + this->nodes.remove(*child); + } + + if (parent != nullptr) parent->recalcSizePosRecursive(); +} + +bool Hy3Layout::isWindowTiled(CWindow* window) { + return this->getNodeFromWindow(window) != nullptr; +} + +void Hy3Layout::recalculateMonitor(const int& eIdleInhibitMode) { + ; // empty +} + +void Hy3Layout::recalculateWindow(CWindow* pWindow) { + ; // empty +} + +void Hy3Layout::resizeActiveWindow(const Vector2D& delta, CWindow* pWindow) { + ; // empty +} + +void Hy3Layout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreenMode mode, bool on) { + ; // empty +} + +std::any Hy3Layout::layoutMessage(SLayoutMessageHeader header, std::string content) { + if (header.pWindow == nullptr) return ""; + auto* node = this->getNodeFromWindow(header.pWindow); + if (node == nullptr) return ""; + + if (content == "splith" || content == "splitv") { + Hy3GroupLayout layout = Hy3GroupLayout::SplitH; + if (content == "splitv") { + layout = Hy3GroupLayout::SplitV; + } + + Hy3NodeData node_data = Hy3NodeData(Hy3GroupData(layout)); + std::swap(node->data, node_data); + + this->nodes.push_back({ + .parent = node, + .data = node_data, + .workspace_id = node->workspace_id, + .layout = this, + }); + + node->data.as_group.children.push_back(&this->nodes.back()); + node->recalcSizePosRecursive(); + } + + return ""; +} + +SWindowRenderLayoutHints Hy3Layout::requestRenderHints(CWindow* pWindow) { + return {}; +} + +void Hy3Layout::switchWindows(CWindow* pWindowA, CWindow* pWindowB) { + ; // empty +} + +void Hy3Layout::alterSplitRatio(CWindow* pWindow, float delta, bool exact) { + ; // empty +} + +std::string Hy3Layout::getLayoutName() { + return "custom"; +} + +void Hy3Layout::replaceWindowDataWith(CWindow* from, CWindow* to) { + ; // empty +} + +void Hy3Layout::onEnable() { + for (auto& w : g_pCompositor->m_vWindows) { + if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut || w->m_bIsFloating) + continue; + + this->onWindowCreatedTiling(w.get()); + } +} + +void Hy3Layout::onDisable() { +} diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp new file mode 100644 index 0000000..7d23e80 --- /dev/null +++ b/src/Hy3Layout.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +class Hy3Layout; +struct Hy3Node; + +enum class Hy3GroupLayout { + SplitH, + SplitV, + Tabbed, +}; + +struct Hy3GroupData { + Hy3GroupLayout layout = Hy3GroupLayout::SplitH; + std::list children; + + Hy3GroupData(Hy3GroupLayout layout); +}; + +struct Hy3NodeData { + enum { Group, Window } type; + union { + Hy3GroupData as_group; + CWindow* as_window; + }; + + bool operator==(const Hy3NodeData&) const; + + Hy3NodeData(); + Hy3NodeData(CWindow* window); + Hy3NodeData(Hy3GroupData group); + Hy3NodeData(const Hy3NodeData&); + Hy3NodeData(Hy3NodeData&&); + Hy3NodeData& operator=(const Hy3NodeData&); + ~Hy3NodeData(); +}; + +struct Hy3Node { + Hy3Node* parent = nullptr; + Hy3NodeData data; + Vector2D position; + Vector2D size; + float size_ratio = 1.0; + int workspace_id = -1; + bool valid = true; + Hy3Layout* layout = nullptr; + + void recalcSizePosRecursive(bool force = false); + + bool operator==(const Hy3Node&) const; +}; + +class Hy3Layout: public IHyprLayout { +public: + virtual void onWindowCreatedTiling(CWindow*); + virtual void onWindowRemovedTiling(CWindow*); + virtual bool isWindowTiled(CWindow*); + virtual void recalculateMonitor(const int&); + virtual void recalculateWindow(CWindow*); + virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr); + virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool); + virtual std::any layoutMessage(SLayoutMessageHeader, std::string); + virtual SWindowRenderLayoutHints requestRenderHints(CWindow*); + virtual void switchWindows(CWindow*, CWindow*); + virtual void alterSplitRatio(CWindow*, float, bool); + virtual std::string getLayoutName(); + virtual void replaceWindowDataWith(CWindow* from, CWindow* to); + + virtual void onEnable(); + virtual void onDisable(); + +private: + // std::list is used over std::vector because it does not invalidate references + // when mutated. + std::list nodes; + + int getWorkspaceNodeCount(const int&); + Hy3Node* getNodeFromWindow(CWindow*); + Hy3Node* getWorkspaceRootGroup(const int&); + void applyNodeDataToWindow(Hy3Node*, bool force = false); + + friend struct Hy3Node; +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..350e61c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,49 @@ +#include + +#include "Hy3Layout.hpp" +#include "src/Compositor.hpp" + +inline HANDLE PHANDLE = nullptr; +inline std::unique_ptr g_Hy3Layout; + +APICALL EXPORT std::string PLUGIN_API_VERSION() { + return HYPRLAND_API_VERSION; +} + +void splith(std::string) { + SLayoutMessageHeader header; + header.pWindow = g_pCompositor->m_pLastWindow; + + if (!header.pWindow) return; + + const auto workspace = g_pCompositor->getWorkspaceByID(header.pWindow->m_iWorkspaceID); + if (workspace->m_bHasFullscreenWindow) return; + + g_pLayoutManager->getCurrentLayout()->layoutMessage(header, "splith"); +} + +void splitv(std::string) { + SLayoutMessageHeader header; + header.pWindow = g_pCompositor->m_pLastWindow; + + if (!header.pWindow) return; + + const auto workspace = g_pCompositor->getWorkspaceByID(header.pWindow->m_iWorkspaceID); + if (workspace->m_bHasFullscreenWindow) return; + + g_pLayoutManager->getCurrentLayout()->layoutMessage(header, "splitv"); +} + +APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { + PHANDLE = handle; + + g_Hy3Layout = std::make_unique(); + HyprlandAPI::addLayout(PHANDLE, "hy3", g_Hy3Layout.get()); + + HyprlandAPI::addDispatcher(PHANDLE, "splith", splith); + HyprlandAPI::addDispatcher(PHANDLE, "splitv", splitv); + + return {"hy3", "i3 like layout for hyprland", "outfoxxed", "0.1"}; +} + +APICALL EXPORT void PLUGIN_EXIT() {}