#define WLR_USE_UNSTABLE #include "globals.hpp" #include <src/Window.hpp> #include <src/layout/DwindleLayout.hpp> #include <src/managers/KeybindManager.hpp> #include <src/managers/LayoutManager.hpp> const CColor s_pluginColor = {0x61 / 255.0f, 0xAF / 255.0f, 0xEF / 255.0f, 1.0f}; inline std::function<void(std::string)> originalToggleGroup = nullptr; typedef SDwindleNodeData* (*nodeFromWindowT)(void*, CWindow*); inline nodeFromWindowT g_pNodeFromWindow = nullptr; void addChildNodesToDequeRecursive(std::deque<SDwindleNodeData*>* pDeque, std::deque<SDwindleNodeData*>* pParents, SDwindleNodeData* node) { if (node->isNode) { pParents->push_back(node); addChildNodesToDequeRecursive(pDeque, pParents, node->children[0]); addChildNodesToDequeRecursive(pDeque, pParents, node->children[1]); } else { pDeque->emplace_back(node); } } void groupDissolve(const SDwindleNodeData* PNODE, CHyprDwindleLayout* layout) { CWindow* PWINDOW = PNODE->pWindow; // This group only has this single winodw if (PWINDOW->m_sGroupData.pNextWindow == PWINDOW) { Debug::log(LOG, "Ignoring autogroup on single window dissolve"); originalToggleGroup(""); return; } Debug::log(LOG, "Dissolving group"); // We currently don't need any special logic for dissolving, just call the original func originalToggleGroup(""); } void groupCreate(const SDwindleNodeData* PNODE, CHyprDwindleLayout* layout) { const auto PWINDOW = PNODE->pWindow; const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PNODE->workspaceID); if (PWORKSPACE->m_bHasFullscreenWindow) { Debug::log(LOG, "Ignoring autogroup on a fullscreen window"); originalToggleGroup(""); return; } if (!PNODE->pParent) { Debug::log(LOG, "Ignoring autogroup for a solitary window"); originalToggleGroup(""); return; } Debug::log(LOG, "Creating group"); // Call the original toggleGroup function, and only do extra things afterwards originalToggleGroup(""); std::deque<SDwindleNodeData*> newGroupMembers; std::deque<SDwindleNodeData*> parentNodes; addChildNodesToDequeRecursive( &newGroupMembers, &parentNodes, PNODE->pParent->children[0] == PNODE ? PNODE->pParent->children[1] : PNODE->pParent->children[0]); // Make sure one of the child nodes isn't itself a group for (auto& n : newGroupMembers) { if (n->pWindow->m_sGroupData.pNextWindow) { Debug::log(LOG, "Ignoring autogroup for nested groups"); return; } } // Add all of the children nodes into the group for (auto& n : newGroupMembers) { auto window = n->pWindow; layout->onWindowRemoved(window); PWINDOW->insertWindowToGroup(window); window->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(window)); PWINDOW->setGroupCurrent(PWINDOW); } } void toggleGroup(std::string args) { // We only care about group creations, not group disolves const auto PWINDOW = g_pCompositor->m_pLastWindow; if (!PWINDOW || !g_pCompositor->windowValidMapped(PWINDOW)) return; // Don't do anything if we're not on "dwindle" layout IHyprLayout* layout = g_pLayoutManager->getCurrentLayout(); if (layout->getLayoutName() != "dwindle") { Debug::log(LOG, "Ignoring autogroup for non-dinwle layout"); originalToggleGroup(args); return; } CHyprDwindleLayout* cur_dwindle = (CHyprDwindleLayout*)layout; const auto PNODE = g_pNodeFromWindow(cur_dwindle, PWINDOW); if (!PNODE) { Debug::log(LOG, "Ignoring autogroup for floating window"); originalToggleGroup(args); return; } if (PWINDOW->m_sGroupData.pNextWindow) { groupDissolve(PNODE, cur_dwindle); } else { groupCreate(PNODE, cur_dwindle); } } // Do NOT change this function. APICALL EXPORT std::string PLUGIN_API_VERSION() { return HYPRLAND_API_VERSION; } APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { PHANDLE = handle; // Get address of the private CHyprDwindleLayout::getNodeFromWindow member function, we'll need it in toggleGroup static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "getNodeFromWindow"); g_pNodeFromWindow = (nodeFromWindowT)METHODS[1].address; for (auto& method : METHODS) { if (method.demangled == "CHyprDwindleLayout::getNodeFromWindow(CWindow*)") { g_pNodeFromWindow = (nodeFromWindowT)method.address; break; } } if (g_pNodeFromWindow == nullptr) { Debug::log(ERR, "getNodeFromWindow funnction for dwindle layout wasn't found! This function's signature probably changed, report this"); HyprlandAPI::addNotification( PHANDLE, "[dwindle-autogroup] Initialization failed!! getNodeFromWindow functio not found", s_pluginColor, 10000); } else { originalToggleGroup = g_pKeybindManager->m_mDispatchers["togglegroup"]; HyprlandAPI::addDispatcher(PHANDLE, "togglegroup", toggleGroup); HyprlandAPI::reloadConfig(); HyprlandAPI::addNotification(PHANDLE, "[dwindle-autogroup] Initialized successfully!", s_pluginColor, 5000); } return {"dwindle-autogroup", "Dwindle Autogroup", "ItsDrike", "1.0"}; } APICALL EXPORT void PLUGIN_EXIT() { // Since we added the "togglegroup" dispatcher ourselves, by default, the cleanup would just remove it // but we want to restore it back to the original function instead HyprlandAPI::removeDispatcher(PHANDLE, "togglegroup"); g_pKeybindManager->m_mDispatchers["togglegroup"] = originalToggleGroup; HyprlandAPI::addNotification(PHANDLE, "[dwindle-autogroup] Unloaded successfully!", s_pluginColor, 5000); }