diff --git a/CMakeLists.txt b/CMakeLists.txt index 419d4f7..34a347c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ project(Hypr ) add_compile_options(-std=c++17) +add_compile_options(-Wall -Wextra) find_package(Threads REQUIRED) file(GLOB_RECURSE SRCFILES "src/*.cpp") diff --git a/README.md b/README.md index e7e54b7..2bdeb73 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Hypr is a Linux tiling window manager for Xorg. It's written in XCB with modern - [x] Workspaces - [x] Moving windows / user input (e.g. fullscreening) ~ More to be done probably - [ ] True lerp animations -- [ ] Rewriting the window system to be a tree +- [x] Rewriting the window system to be a tree
diff --git a/src/events/events.cpp b/src/events/events.cpp index f975e43..f57a840 100644 --- a/src/events/events.cpp +++ b/src/events/events.cpp @@ -69,6 +69,50 @@ void Events::eventMapWindow(xcb_generic_event_t* event) { window.setDefaultPosition(Vector2D(0,0)); window.setDefaultSize(Vector2D(g_pWindowManager->Screen->width_in_pixels/2.f,g_pWindowManager->Screen->height_in_pixels/2.f)); + // Set the parent + // check if lastwindow is on our workspace + if (auto PLASTWINDOW = g_pWindowManager->getWindowFromDrawable(g_pWindowManager->LastWindow); PLASTWINDOW + && PLASTWINDOW->getWorkspaceID() == g_pWindowManager->activeWorkspaces[CURRENTSCREEN]) { + // LastWindow is on our workspace, let's make a new split node + + if (PLASTWINDOW->getIsFloating()) { + // find a window manually + PLASTWINDOW = g_pWindowManager->findWindowAtCursor(); + } + + CWindow newWindowSplitNode; + newWindowSplitNode.setPosition(PLASTWINDOW->getPosition()); + newWindowSplitNode.setSize(PLASTWINDOW->getSize()); + + newWindowSplitNode.setChildNodeAID(PLASTWINDOW->getDrawable()); + newWindowSplitNode.setChildNodeBID(E->window); + + newWindowSplitNode.setParentNodeID(PLASTWINDOW->getParentNodeID()); + + newWindowSplitNode.setWorkspaceID(window.getWorkspaceID()); + newWindowSplitNode.setMonitor(window.getMonitor()); + + // generates a negative node ID + newWindowSplitNode.generateNodeID(); + + // update the parent if exists + if (const auto PREVPARENT = g_pWindowManager->getWindowFromDrawable(PLASTWINDOW->getParentNodeID()); PREVPARENT) { + if (PREVPARENT->getChildNodeAID() == PLASTWINDOW->getDrawable()) { + PREVPARENT->setChildNodeAID(newWindowSplitNode.getDrawable()); + } else { + PREVPARENT->setChildNodeBID(newWindowSplitNode.getDrawable()); + } + } + + window.setParentNodeID(newWindowSplitNode.getDrawable()); + PLASTWINDOW->setParentNodeID(newWindowSplitNode.getDrawable()); + + g_pWindowManager->addWindowToVectorSafe(newWindowSplitNode); + } else { + // LastWindow is not on our workspace, so set the parent to 0. + window.setParentNodeID(0); + } + // Also sets the old one g_pWindowManager->calculateNewWindowParams(&window); diff --git a/src/window.cpp b/src/window.cpp index d6d7de2..a1b6199 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,4 +1,48 @@ #include "window.hpp" +#include "windowManager.hpp" -CWindow::CWindow() { this->setDirty(true); this->setFullscreen(false); this->setIsFloating(false); } -CWindow::~CWindow() { } \ No newline at end of file +CWindow::CWindow() { this->setDirty(true); this->setFullscreen(false); this->setIsFloating(false); this->setParentNodeID(0); this->setChildNodeAID(0); this->setChildNodeBID(0); } +CWindow::~CWindow() { } + +void CWindow::generateNodeID() { + + int64_t lowest = -1; + for (auto& window : g_pWindowManager->windows) { + if ((int64_t)window.getDrawable() < lowest) { + lowest = (int64_t)window.getDrawable(); + } + } + + m_iDrawable = lowest - 1; +} + +void CWindow::setDirtyRecursive(bool val) { + m_bDirty = val; + + if (m_iChildNodeAID != 0) { + g_pWindowManager->getWindowFromDrawable(m_iChildNodeAID)->setDirtyRecursive(val); + g_pWindowManager->getWindowFromDrawable(m_iChildNodeBID)->setDirtyRecursive(val); + } +} + +void CWindow::recalcSizePosRecursive() { + if (m_iChildNodeAID != 0) { + const auto HORIZONTAL = m_vecSize.x > m_vecSize.y; + g_pWindowManager->getWindowFromDrawable(m_iChildNodeAID)->setPosition(m_vecPosition); + g_pWindowManager->getWindowFromDrawable(m_iChildNodeBID)->setPosition(m_vecPosition + (HORIZONTAL ? Vector2D(m_vecSize.x / 2.f, 0) : Vector2D(0, m_vecSize.y / 2.f))); + + g_pWindowManager->getWindowFromDrawable(m_iChildNodeAID)->setSize(Vector2D(m_vecSize.x / (HORIZONTAL ? 2 : 1), m_vecSize.y / (HORIZONTAL ? 1 : 2))); + g_pWindowManager->getWindowFromDrawable(m_iChildNodeBID)->setSize(Vector2D(m_vecSize.x / (HORIZONTAL ? 2 : 1), m_vecSize.y / (HORIZONTAL ? 1 : 2))); + + g_pWindowManager->getWindowFromDrawable(m_iChildNodeAID)->setDirty(true); + g_pWindowManager->getWindowFromDrawable(m_iChildNodeBID)->setDirty(true); + + if (m_iChildNodeAID < 0) { + g_pWindowManager->getWindowFromDrawable(m_iChildNodeAID)->recalcSizePosRecursive(); + } + + if (m_iChildNodeBID < 0) { + g_pWindowManager->getWindowFromDrawable(m_iChildNodeBID)->recalcSizePosRecursive(); + } + } +} \ No newline at end of file diff --git a/src/window.hpp b/src/window.hpp index 554d8c1..d808b6d 100644 --- a/src/window.hpp +++ b/src/window.hpp @@ -14,17 +14,38 @@ public: void resize(Vector2D size); void resize(float percx, float percy); + // ------------------------------------- // + // Node Stuff // + // ------------------------------------- // + + // IDs: + // > 0 : Windows + // == 0 : None + // < 0 : Nodes + + EXPOSED_MEMBER(ParentNodeID, int64_t, i); + + EXPOSED_MEMBER(ChildNodeAID, int64_t, i); + EXPOSED_MEMBER(ChildNodeBID, int64_t, i); + + void generateNodeID(); + + // ------------------------------------- // + std::string getName(); // Tells the window manager to reload the window's params EXPOSED_MEMBER(Dirty, bool, b); + void setDirtyRecursive(bool); + void recalcSizePosRecursive(); + EXPOSED_MEMBER(Size, Vector2D, vec); EXPOSED_MEMBER(EffectiveSize, Vector2D, vec); EXPOSED_MEMBER(EffectivePosition, Vector2D, vec); EXPOSED_MEMBER(Position, Vector2D, vec); EXPOSED_MEMBER(IsFloating, bool, b); - EXPOSED_MEMBER(Drawable, xcb_drawable_t, i); + EXPOSED_MEMBER(Drawable, int64_t, i); // int64_t because it's my internal ID system too. // Fullscreen EXPOSED_MEMBER(Fullscreen, bool, b); diff --git a/src/windowManager.cpp b/src/windowManager.cpp index d92648a..cf94b5a 100644 --- a/src/windowManager.cpp +++ b/src/windowManager.cpp @@ -55,7 +55,7 @@ void CWindowManager::setupRandrMonitors() { } Debug::log(NONE, "Monitor " + monitors[monitors.size() - 1].szName + ": " + std::to_string(monitors[i].vecSize.x) + "x" + std::to_string(monitors[monitors.size() - 1].vecSize.y) + - ", at " + std::to_string(monitors[monitors.size() - 1].vecPosition.x) + "," + std::to_string(monitors[monitors.size() - 1].vecSize.y)); + ", at " + std::to_string(monitors[monitors.size() - 1].vecPosition.x) + "," + std::to_string(monitors[monitors.size() - 1].vecPosition.y) + ", ID: " + std::to_string(monitors[monitors.size() - 1].ID)); } const auto EXTENSIONREPLY = xcb_get_extension_data(DisplayConnection, &xcb_randr_id); @@ -65,31 +65,29 @@ void CWindowManager::setupRandrMonitors() { //listen for screen change events xcb_randr_select_input(DisplayConnection, Screen->root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE); } - - // Sort monitors for my convenience thanks - std::sort(monitors.begin(), monitors.end(), [](SMonitor& a, SMonitor& b) { - return a.vecPosition.x < b.vecPosition.x; - }); } void CWindowManager::setupManager() { setupRandrMonitors(); - ConfigManager::init(); - if (monitors.size() == 0) { // RandR failed! Debug::log(WARN, "RandR failed!"); - monitors.push_back(SMonitor()); - monitors[0].vecPosition = Vector2D(0, 0); - monitors[0].vecSize = Vector2D(Screen->width_in_pixels, Screen->height_in_pixels); - monitors[0].ID = 0; - monitors[0].szName = "Screen"; + #define TESTING_MON_AMOUNT 3 + for (int i = 0; i < TESTING_MON_AMOUNT /* Testing on 3 monitors, RandR shouldnt fail on a real desktop */; ++i) { + monitors.push_back(SMonitor()); + monitors[i].vecPosition = Vector2D(i * Screen->width_in_pixels / TESTING_MON_AMOUNT, 0); + monitors[i].vecSize = Vector2D(Screen->width_in_pixels / TESTING_MON_AMOUNT, Screen->height_in_pixels); + monitors[i].ID = i; + monitors[i].szName = "Screen" + std::to_string(i); + } } Debug::log(LOG, "RandR done."); + ConfigManager::init(); + Values[0] = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; xcb_change_window_attributes_checked(DisplayConnection, Screen->root, XCB_CW_EVENT_MASK, Values); @@ -118,7 +116,7 @@ void CWindowManager::setupManager() { Debug::log(LOG, "Keys done."); // Add workspaces to the monitors - for (int i = 0; i < monitors.size(); ++i) { + for (long unsigned int i = 0; i < monitors.size(); ++i) { CWorkspace protoWorkspace; protoWorkspace.setID(i + 1); protoWorkspace.setMonitor(i); @@ -216,35 +214,6 @@ bool CWindowManager::handleEvent() { return true; } -// TODO: add an empty space check -void CWindowManager::performSanityCheckForWorkspace(int WorkspaceID) { - for (auto& windowA : windows) { - if (windowA.getWorkspaceID() != WorkspaceID) - continue; - - for (auto& windowB : windows) { - if (windowB.getWorkspaceID() != WorkspaceID) - continue; - - if (windowB.getDrawable() == windowA.getDrawable()) - continue; - - // Check if A and B overlap, if don't, continue - if ((windowA.getPosition().x >= (windowB.getPosition() + windowB.getSize()).x || windowB.getPosition().x >= (windowA.getPosition() + windowA.getSize()).x - || windowA.getPosition().y >= (windowB.getPosition() + windowB.getSize()).y || windowB.getPosition().y >= (windowA.getPosition() + windowA.getSize()).y)) { - continue; - } - - // Overlap detected! Fix window B - if (windowB.getIsFloating()) { - calculateNewTileSetOldTile(&windowB); - } else { - calculateNewFloatingWindow(&windowB); - } - } - } -} - void CWindowManager::cleanupUnusedWorkspaces() { std::vector temp = workspaces; @@ -279,6 +248,12 @@ void CWindowManager::refreshDirtyWindows() { for(auto& window : windows) { if (window.getDirty()) { + // Check if the window isn't a node + if (window.getChildNodeAID() != 0) { + window.setDirty(false); + continue; + } + setEffectiveSizePosUsingConfig(&window); // Fullscreen flag @@ -367,7 +342,7 @@ void CWindowManager::setFocusedWindow(xcb_drawable_t window) { } } -CWindow* CWindowManager::getWindowFromDrawable(xcb_drawable_t window) { +CWindow* CWindowManager::getWindowFromDrawable(int64_t window) { for(auto& w : windows) { if (w.getDrawable() == window) { return &w; @@ -420,9 +395,9 @@ void CWindowManager::setEffectiveSizePosUsingConfig(CWindow* pWindow) { //TODO: make windows with no bar taller, this aint working chief // do gaps, set top left - pWindow->setEffectivePosition(pWindow->getEffectivePosition() + Vector2D(DISPLAYLEFT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYTOP ? ConfigManager::getInt("gaps_out") + (MONITOR->ID != statusBar.getMonitorID() ? ConfigManager::getInt("bar_height") : 0) : ConfigManager::getInt("gaps_in"))); + pWindow->setEffectivePosition(pWindow->getEffectivePosition() + Vector2D(DISPLAYLEFT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYTOP ? ConfigManager::getInt("gaps_out") + (MONITOR->ID == statusBar.getMonitorID() ? ConfigManager::getInt("bar_height") : 0) : ConfigManager::getInt("gaps_in"))); // fix to old size bottom right - pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYLEFT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYTOP ? ConfigManager::getInt("gaps_out") + (MONITOR->ID != statusBar.getMonitorID() ? ConfigManager::getInt("bar_height") : 0) : ConfigManager::getInt("gaps_in"))); + pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYLEFT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYTOP ? ConfigManager::getInt("gaps_out") + (MONITOR->ID == statusBar.getMonitorID() ? ConfigManager::getInt("bar_height") : 0) : ConfigManager::getInt("gaps_in"))); // set bottom right pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYRIGHT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYBOTTOM ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"))); } @@ -459,33 +434,45 @@ CWindow* CWindowManager::findWindowAtCursor() { } void CWindowManager::calculateNewTileSetOldTile(CWindow* pWindow) { - auto PLASTWINDOW = getWindowFromDrawable(LastWindow); + + // Get the parent and both children, one of which will be pWindow + const auto PPARENT = getWindowFromDrawable(pWindow->getParentNodeID()); - if (PLASTWINDOW && (PLASTWINDOW->getIsFloating() || PLASTWINDOW->getWorkspaceID() != pWindow->getWorkspaceID())) { - // find a window manually - PLASTWINDOW = findWindowAtCursor(); + if (!PPARENT) { + // New window on this workspace. + // Open a fullscreen window. + const auto MONITOR = getMonitorFromCursor(); + pWindow->setSize(Vector2D(MONITOR->vecSize.x, MONITOR->vecSize.y)); + pWindow->setPosition(Vector2D(MONITOR->vecPosition.x, MONITOR->vecPosition.y)); + + return; } - if (PLASTWINDOW) { - const auto PLASTSIZE = PLASTWINDOW->getSize(); - const auto PLASTPOS = PLASTWINDOW->getPosition(); + // Get the sibling + const auto PSIBLING = getWindowFromDrawable(PPARENT->getChildNodeAID() == pWindow->getDrawable() ? PPARENT->getChildNodeBID() : PPARENT->getChildNodeAID()); + + // Should NEVER be null + if (PSIBLING) { + const auto PLASTSIZE = PPARENT->getSize(); + const auto PLASTPOS = PPARENT->getPosition(); if (PLASTSIZE.x > PLASTSIZE.y) { - PLASTWINDOW->setSize(Vector2D(PLASTSIZE.x / 2.f, PLASTSIZE.y)); + PSIBLING->setPosition(Vector2D(PLASTPOS.x, PLASTPOS.y)); + PSIBLING->setSize(Vector2D(PLASTSIZE.x / 2.f, PLASTSIZE.y)); pWindow->setSize(Vector2D(PLASTSIZE.x / 2.f, PLASTSIZE.y)); pWindow->setPosition(Vector2D(PLASTPOS.x + PLASTSIZE.x / 2.f, PLASTPOS.y)); } else { - PLASTWINDOW->setSize(Vector2D(PLASTSIZE.x, PLASTSIZE.y / 2.f)); + PSIBLING->setPosition(Vector2D(PLASTPOS.x, PLASTPOS.y)); + PSIBLING->setSize(Vector2D(PLASTSIZE.x, PLASTSIZE.y / 2.f)); pWindow->setSize(Vector2D(PLASTSIZE.x, PLASTSIZE.y / 2.f)); pWindow->setPosition(Vector2D(PLASTPOS.x, PLASTPOS.y + PLASTSIZE.y / 2.f)); } - PLASTWINDOW->setDirty(true); + PSIBLING->setDirty(true); } else { - // Open a fullscreen window - const auto MONITOR = getMonitorFromCursor(); - pWindow->setSize(Vector2D(MONITOR->vecSize.x, MONITOR->vecSize.y)); - pWindow->setPosition(Vector2D(MONITOR->vecPosition.x, MONITOR->vecPosition.y)); + Debug::log(ERR, "Sibling node was null?? pWindow x,y,w,h: " + std::to_string(pWindow->getPosition().x) + " " + + std::to_string(pWindow->getPosition().y) + " " + std::to_string(pWindow->getSize().x) + " " + + std::to_string(pWindow->getSize().y)); } } @@ -593,32 +580,39 @@ void CWindowManager::fixWindowOnClose(CWindow* pClosedWindow) { if (!pClosedWindow) return; - const auto WORKSPACE = activeWorkspaces[getMonitorFromWindow(pClosedWindow)->ID]; + // Get the parent and both children, one of which will be pWindow + const auto PPARENT = getWindowFromDrawable(pClosedWindow->getParentNodeID()); - // Fix if was fullscreen - if (pClosedWindow->getFullscreen()) - g_pWindowManager->getWorkspaceByID(WORKSPACE)->setHasFullscreenWindow(false); + if (!PPARENT) + return; // if there was no parent, we do not need to update anything. it was a fullscreen window, the only one on a given workspace. - // get the first neighboring window - CWindow* neighbor = nullptr; - for(auto& w : windows) { - if (w.getDrawable() == pClosedWindow->getDrawable()) - continue; + // Get the sibling + const auto PSIBLING = getWindowFromDrawable(PPARENT->getChildNodeAID() == pClosedWindow->getDrawable() ? PPARENT->getChildNodeBID() : PPARENT->getChildNodeAID()); - if (isNeighbor(&w, pClosedWindow) && canEatWindow(&w, pClosedWindow)) { - neighbor = &w; - break; - } + PSIBLING->setPosition(PPARENT->getPosition()); + PSIBLING->setSize(PPARENT->getSize()); + + // make the sibling replace the parent + PSIBLING->setParentNodeID(PPARENT->getParentNodeID()); + + if (PPARENT->getParentNodeID() != 0 + && getWindowFromDrawable(PPARENT->getParentNodeID())) { + if (getWindowFromDrawable(PPARENT->getParentNodeID())->getChildNodeAID() == PPARENT->getDrawable()) { + getWindowFromDrawable(PPARENT->getParentNodeID())->setChildNodeAID(PSIBLING->getDrawable()); + } else { + getWindowFromDrawable(PPARENT->getParentNodeID())->setChildNodeBID(PSIBLING->getDrawable()); + } } - if (!neighbor) - return; // No neighbor. Don't update, easy. + // Make the sibling eat the closed window + PSIBLING->setDirtyRecursive(true); + PSIBLING->recalcSizePosRecursive(); - // update neighbor to "eat" closed. - eatWindow(neighbor, pClosedWindow); + // Remove the parent + removeWindowFromVectorSafe(PPARENT->getDrawable()); - neighbor->setDirty(true); - setFocusedWindow(neighbor->getDrawable()); // Set focus. :) + if (findWindowAtCursor()) + setFocusedWindow(findWindowAtCursor()->getDrawable()); // Set focus. :) } CWindow* CWindowManager::getNeighborInDir(char dir) { @@ -717,9 +711,6 @@ void CWindowManager::changeWorkspaceByID(int ID) { activeWorkspaces[workspace.getMonitor()] = workspace.getID(); LastWindow = -1; - // Perform a sanity check for the new workspace - performSanityCheckForWorkspace(ID); - // Update bar info updateBarInfo(); @@ -738,9 +729,6 @@ void CWindowManager::changeWorkspaceByID(int ID) { activeWorkspaces[MONITOR->ID] = workspaces[workspaces.size() - 1].getID(); LastWindow = -1; - // Perform a sanity check for the new workspace - performSanityCheckForWorkspace(ID); - // Update bar info updateBarInfo(); diff --git a/src/windowManager.hpp b/src/windowManager.hpp index 89ae51f..317f50b 100644 --- a/src/windowManager.hpp +++ b/src/windowManager.hpp @@ -37,14 +37,13 @@ public: CStatusBar statusBar; std::thread* barThread; - CWindow* getWindowFromDrawable(xcb_drawable_t); + CWindow* getWindowFromDrawable(int64_t); void addWindowToVectorSafe(CWindow); void removeWindowFromVectorSafe(xcb_drawable_t); void setupManager(); bool handleEvent(); void refreshDirtyWindows(); - void performSanityCheckForWorkspace(int); void setFocusedWindow(xcb_drawable_t); @@ -65,6 +64,9 @@ public: SMonitor* getMonitorFromWindow(CWindow*); SMonitor* getMonitorFromCursor(); + // finds a window that's tiled at cursor. + CWindow* findWindowAtCursor(); + private: // Internal WM functions that don't have to be exposed @@ -77,7 +79,6 @@ public: bool isNeighbor(CWindow* a, CWindow* b); void calculateNewTileSetOldTile(CWindow* pWindow); void calculateNewFloatingWindow(CWindow* pWindow); - CWindow* findWindowAtCursor(); void setEffectiveSizePosUsingConfig(CWindow* pWindow); void cleanupUnusedWorkspaces(); xcb_visualtype_t* setupColors();