#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; if (group->children.size() == 1 && this->parent != nullptr) { auto& child = group->children.front(); double distortOut; double distortIn; const auto* gaps_in = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue; const auto* gaps_out = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue; if (gaps_in > gaps_out) { distortOut = *gaps_out - 1.0; } else { distortOut = *gaps_in - 1.0; } if (distortOut < 0) distortOut = 0.0; distortIn = *gaps_in * 2; switch (group->layout) { case Hy3GroupLayout::SplitH: child->position.x = this->position.x - distortOut; child->size.x = this->size.x - distortIn; child->position.y = this->position.y; child->size.y = this->size.y; break; case Hy3GroupLayout::SplitV: child->position.y = this->position.y - distortOut; child->size.y = this->size.y - distortIn; child->position.x = this->position.x; child->size.x = this->size.x; case Hy3GroupLayout::Tabbed: // TODO break; } child->recalcSizePosRecursive(); return; } 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(); } void Hy3Layout::onWindowFocusChange(CWindow* window) { Debug::log(LOG, "Switched windows from %p to %p", this->lastActiveWindow, window); auto* node = this->getNodeFromWindow(this->lastActiveWindow); while (node != nullptr && node->parent != nullptr && node->parent->parent != nullptr && node->parent->data.as_group.children.size() == 1) { auto parent = node->parent; std::swap(parent->data, node->data); this->nodes.remove(*node); node = parent; } if (node != nullptr) { node->recalcSizePosRecursive(); } this->lastActiveWindow = window; } 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; } if (node->parent != nullptr && node->parent->data.as_group.children.size() == 1 && (node->parent->data.as_group.layout == Hy3GroupLayout::SplitH || node->parent->data.as_group.layout == Hy3GroupLayout::SplitV)) { node->parent->data.as_group.layout = layout; node->parent->recalcSizePosRecursive(); return ""; } 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() { }