From 7b7ba35b0ce1a49bb3bd13f453f05dcf3de5fded Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Mon, 22 Nov 2021 21:20:32 +0100 Subject: [PATCH] =?UTF-8?q?added=20floating=20windows=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/KeybindManager.cpp | 22 ++++- src/bar/Bar.cpp | 5 ++ src/config/ConfigManager.cpp | 1 + src/events/events.cpp | 156 ++++++++++++++++++++++++++++------- src/events/events.hpp | 5 ++ src/utilities/Keybind.hpp | 3 +- src/windowManager.cpp | 77 ++++++++++------- src/windowManager.hpp | 5 ++ 9 files changed, 212 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 2bdeb73..f41b641 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Hypr is a Linux tiling window manager for Xorg. It's written in XCB with modern ## Roadmap (not in order) - [x] Multi-monitor support - [x] Status bar ~ Needs expanding -- [ ] Floating windows support (Very basic for now) +- [x] Floating windows support - [x] Config system ~ Basic done - [x] Workspaces - [x] Moving windows / user input (e.g. fullscreening) ~ More to be done probably diff --git a/src/KeybindManager.cpp b/src/KeybindManager.cpp index c64285d..4c2b2ea 100644 --- a/src/KeybindManager.cpp +++ b/src/KeybindManager.cpp @@ -1,5 +1,6 @@ #include "KeybindManager.hpp" #include "utilities/Util.hpp" +#include "events/events.hpp" #include #include @@ -57,6 +58,8 @@ unsigned int KeybindManager::modToMask(MODS mod) { return XCB_MOD_MASK_4; case MOD_SHIFT: return XCB_MOD_MASK_SHIFT; + case MOD_SHIFTSUPER: + return XCB_MOD_MASK_4 | XCB_MOD_MASK_SHIFT; } return 0; @@ -137,9 +140,24 @@ void KeybindManager::toggleActiveWindowFloating(std::string unusedArg) { PWINDOW->setDirty(true); // Fix window as if it's closed if we just made it floating - if (PWINDOW->getIsFloating()) + if (PWINDOW->getIsFloating()) { g_pWindowManager->fixWindowOnClose(PWINDOW); + g_pWindowManager->calculateNewWindowParams(PWINDOW); + } + else { + // It's remapped again - g_pWindowManager->calculateNewWindowParams(PWINDOW); + // SAVE ALL INFO NOW, THE POINTER WILL BE DEAD + const auto RESTOREACSIZE = PWINDOW->getDefaultSize(); + const auto RESTOREACPOS = PWINDOW->getDefaultPosition(); + const auto RESTOREWINID = PWINDOW->getDrawable(); + + g_pWindowManager->removeWindowFromVectorSafe(PWINDOW->getDrawable()); + const auto PNEWWINDOW = Events::remapWindow(RESTOREWINID, true); + + PNEWWINDOW->setDefaultPosition(RESTOREACPOS); + PNEWWINDOW->setDefaultSize(RESTOREACSIZE); + } + } } \ No newline at end of file diff --git a/src/bar/Bar.cpp b/src/bar/Bar.cpp index 8f446ab..d3c0d9b 100644 --- a/src/bar/Bar.cpp +++ b/src/bar/Bar.cpp @@ -84,6 +84,11 @@ void CStatusBar::setup(int MonitorID) { // don't, i use it later //xcb_close_font(g_pWindowManager->DisplayConnection, contextBASETEXT->Font); + + + // Set the bar to be top + values[0] = XCB_STACK_MODE_ABOVE; + xcb_configure_window(g_pWindowManager->DisplayConnection, m_iWindowID, XCB_CONFIG_WINDOW_STACK_MODE, values); } void CStatusBar::destroy() { diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index f1215d2..768b5bb 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -49,6 +49,7 @@ void handleBind(const std::string& command, const std::string& value) { if (MOD == "SUPER") mod = MOD_SUPER; else if (MOD == "SHIFT") mod = MOD_SHIFT; + else if (MOD == "SUPERSHIFT" || MOD == "SHIFTSUPER") mod = MOD_SHIFTSUPER; Dispatcher dispatcher = nullptr; if (HANDLER == "exec") dispatcher = KeybindManager::call; diff --git a/src/events/events.cpp b/src/events/events.cpp index f57a840..fa31b7c 100644 --- a/src/events/events.cpp +++ b/src/events/events.cpp @@ -37,9 +37,9 @@ void Events::eventDestroy(xcb_generic_event_t* event) { const auto E = reinterpret_cast(event); xcb_kill_client(g_pWindowManager->DisplayConnection, E->window); - // fix last window + // fix last window if tile const auto CLOSEDWINDOW = g_pWindowManager->getWindowFromDrawable(E->window); - if (CLOSEDWINDOW) { + if (CLOSEDWINDOW && !CLOSEDWINDOW->getIsFloating()) { g_pWindowManager->fixWindowOnClose(CLOSEDWINDOW); // delete off of the arr @@ -47,45 +47,36 @@ void Events::eventDestroy(xcb_generic_event_t* event) { } } -void Events::eventMapWindow(xcb_generic_event_t* event) { - const auto E = reinterpret_cast(event); - - // make sure it's not the bar! - if (E->window == g_pWindowManager->statusBar.getWindowID()) - return; - - // Map the window - xcb_map_window(g_pWindowManager->DisplayConnection, E->window); - +CWindow* Events::remapWindow(int windowID, bool wasfloating) { // Do the setup of the window's params and stuf CWindow window; - window.setDrawable(E->window); + window.setDrawable(windowID); window.setIsFloating(false); window.setDirty(true); const auto CURRENTSCREEN = g_pWindowManager->getMonitorFromCursor()->ID; window.setWorkspaceID(g_pWindowManager->activeWorkspaces[CURRENTSCREEN]); window.setMonitor(CURRENTSCREEN); - window.setDefaultPosition(Vector2D(0,0)); - window.setDefaultSize(Vector2D(g_pWindowManager->Screen->width_in_pixels/2.f,g_pWindowManager->Screen->height_in_pixels/2.f)); + 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 (auto PLASTWINDOW = g_pWindowManager->getWindowFromDrawable(g_pWindowManager->LastWindow); (PLASTWINDOW && PLASTWINDOW->getWorkspaceID() == g_pWindowManager->activeWorkspaces[CURRENTSCREEN]) || wasfloating) { + // LastWindow is on our workspace, let's make a new split node - if (PLASTWINDOW->getIsFloating()) { - // find a window manually - PLASTWINDOW = g_pWindowManager->findWindowAtCursor(); - } + if (wasfloating || PLASTWINDOW->getIsFloating()) { + // find a window manually + PLASTWINDOW = g_pWindowManager->findWindowAtCursor(); + } + if (PLASTWINDOW) { CWindow newWindowSplitNode; newWindowSplitNode.setPosition(PLASTWINDOW->getPosition()); newWindowSplitNode.setSize(PLASTWINDOW->getSize()); newWindowSplitNode.setChildNodeAID(PLASTWINDOW->getDrawable()); - newWindowSplitNode.setChildNodeBID(E->window); + newWindowSplitNode.setChildNodeBID(windowID); newWindowSplitNode.setParentNodeID(PLASTWINDOW->getParentNodeID()); @@ -108,6 +99,9 @@ void Events::eventMapWindow(xcb_generic_event_t* event) { PLASTWINDOW->setParentNodeID(newWindowSplitNode.getDrawable()); g_pWindowManager->addWindowToVectorSafe(newWindowSplitNode); + } else { + window.setParentNodeID(0); + } } else { // LastWindow is not on our workspace, so set the parent to 0. window.setParentNodeID(0); @@ -117,22 +111,69 @@ void Events::eventMapWindow(xcb_generic_event_t* event) { g_pWindowManager->calculateNewWindowParams(&window); // Focus - g_pWindowManager->setFocusedWindow(E->window); + g_pWindowManager->setFocusedWindow(windowID); // Add to arr g_pWindowManager->addWindowToVectorSafe(window); - Debug::log(LOG, "Created a new window! X: " + std::to_string(window.getPosition().x) + ", Y: " + std::to_string(window.getPosition().y) + ", W: " - + std::to_string(window.getSize().x) + ", H:" + std::to_string(window.getSize().y) + " ID: " + std::to_string(E->window)); + Debug::log(LOG, "Created a new window! X: " + std::to_string(window.getPosition().x) + ", Y: " + std::to_string(window.getPosition().y) + ", W: " + std::to_string(window.getSize().x) + ", H:" + std::to_string(window.getSize().y) + " ID: " + std::to_string(windowID)); // Set map values g_pWindowManager->Values[0] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE; - xcb_change_window_attributes_checked(g_pWindowManager->DisplayConnection, E->window, XCB_CW_EVENT_MASK, g_pWindowManager->Values); - - g_pWindowManager->setFocusedWindow(E->window); + xcb_change_window_attributes_checked(g_pWindowManager->DisplayConnection, windowID, XCB_CW_EVENT_MASK, g_pWindowManager->Values); + + g_pWindowManager->setFocusedWindow(windowID); + + float values[1]; + values[0] = XCB_STACK_MODE_BELOW; + xcb_configure_window(g_pWindowManager->DisplayConnection, windowID, XCB_CONFIG_WINDOW_STACK_MODE, values); + + return g_pWindowManager->getWindowFromDrawable(windowID); +} + +void Events::eventMapWindow(xcb_generic_event_t* event) { + const auto E = reinterpret_cast(event); + + // make sure it's not the bar! + if (E->window == g_pWindowManager->statusBar.getWindowID()) + return; + + // Map the window + xcb_map_window(g_pWindowManager->DisplayConnection, E->window); + + remapWindow(E->window); } void Events::eventButtonPress(xcb_generic_event_t* event) { + const auto E = reinterpret_cast(event); + + // mouse down! + g_pWindowManager->mouseKeyDown = E->detail; + xcb_grab_pointer(g_pWindowManager->DisplayConnection, 0, g_pWindowManager->Screen->root, XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, + g_pWindowManager->Screen->root, XCB_NONE, XCB_CURRENT_TIME); + + if (const auto PLASTWINDOW = g_pWindowManager->getWindowFromDrawable(g_pWindowManager->LastWindow); PLASTWINDOW) { + if (PLASTWINDOW->getIsFloating()) { + g_pWindowManager->actingOnWindowFloating = PLASTWINDOW->getDrawable(); + g_pWindowManager->mouseLastPos = g_pWindowManager->getCursorPos(); + } + } +} + +void Events::eventButtonRelease(xcb_generic_event_t* event) { + const auto E = reinterpret_cast(event); + + // ungrab the mouse ptr + xcb_ungrab_pointer(g_pWindowManager->DisplayConnection, XCB_CURRENT_TIME); + const auto PACTINGWINDOW = g_pWindowManager->getWindowFromDrawable(g_pWindowManager->actingOnWindowFloating); + if (PACTINGWINDOW) + PACTINGWINDOW->setDirty(true); + g_pWindowManager->actingOnWindowFloating = 0; + g_pWindowManager->mouseKeyDown = 0; +} + +void Events::eventKeyPress(xcb_generic_event_t* event) { const auto E = reinterpret_cast(event); const auto KEYSYM = KeybindManager::getKeysymFromKeycode(E->detail); @@ -146,10 +187,61 @@ void Events::eventButtonPress(xcb_generic_event_t* event) { } } -void Events::eventKeyPress(xcb_generic_event_t* event) { - const auto E = reinterpret_cast(event); +void Events::eventMotionNotify(xcb_generic_event_t* event) { + const auto E = reinterpret_cast(event); - // todo: super resize and move floating + if (!g_pWindowManager->mouseKeyDown) + return; // mouse up. + + if (!g_pWindowManager->actingOnWindowFloating) + return; // not acting, return. + + // means we are holding super + const auto POINTERPOS = g_pWindowManager->getCursorPos(); + const auto POINTERDELTA = Vector2D(POINTERPOS) - g_pWindowManager->mouseLastPos; + + const auto PACTINGWINDOW = g_pWindowManager->getWindowFromDrawable(g_pWindowManager->actingOnWindowFloating); + + if (!PACTINGWINDOW) { + Debug::log(ERR, "ActingWindow not null but doesn't exist?? (Died?)"); + g_pWindowManager->actingOnWindowFloating = 0; + return; + } + + float Values[2]; + + if (abs(POINTERDELTA.x) < 1 && abs(POINTERDELTA.y) < 1) + return; // micromovements + + if (g_pWindowManager->mouseKeyDown == 1) { + // moving + PACTINGWINDOW->setPosition(PACTINGWINDOW->getPosition() + POINTERDELTA); + PACTINGWINDOW->setEffectivePosition(PACTINGWINDOW->getPosition()); + PACTINGWINDOW->setDefaultPosition(PACTINGWINDOW->getPosition()); + + // update workspace if needed + if (g_pWindowManager->getMonitorFromCursor()) { + const auto WORKSPACE = g_pWindowManager->activeWorkspaces[g_pWindowManager->getMonitorFromCursor()->ID]; + PACTINGWINDOW->setWorkspaceID(WORKSPACE); + } else { + PACTINGWINDOW->setWorkspaceID(-1); + } + + PACTINGWINDOW->setDirty(true); + } else if (g_pWindowManager->mouseKeyDown == 3) { + // resizing + PACTINGWINDOW->setSize(PACTINGWINDOW->getSize() + POINTERDELTA); + // clamp + PACTINGWINDOW->setSize(Vector2D(std::clamp(PACTINGWINDOW->getSize().x, (double)30, (double)999999), std::clamp(PACTINGWINDOW->getSize().y, (double)30, (double)999999))); + + // apply to other + PACTINGWINDOW->setDefaultSize(PACTINGWINDOW->getSize()); + PACTINGWINDOW->setEffectiveSize(PACTINGWINDOW->getSize()); + + PACTINGWINDOW->setDirty(true); + } + + g_pWindowManager->mouseLastPos = POINTERPOS; } void Events::eventExpose(xcb_generic_event_t* event) { diff --git a/src/events/events.hpp b/src/events/events.hpp index 80bf0a2..b477a3d 100644 --- a/src/events/events.hpp +++ b/src/events/events.hpp @@ -10,8 +10,13 @@ namespace Events { EVENT(Destroy); EVENT(MapWindow); EVENT(ButtonPress); + EVENT(ButtonRelease); EVENT(Expose); EVENT(KeyPress); + EVENT(MotionNotify); + + // Bypass some events for floating windows + CWindow* remapWindow(int, bool floating = false); // A thread to notify xcb to redraw our shiz void redraw(); diff --git a/src/utilities/Keybind.hpp b/src/utilities/Keybind.hpp index e8a811c..72c8a13 100644 --- a/src/utilities/Keybind.hpp +++ b/src/utilities/Keybind.hpp @@ -6,7 +6,8 @@ typedef void (*Dispatcher)(std::string); enum MODS { MOD_NONE = 0, MOD_SUPER, - MOD_SHIFT + MOD_SHIFT, + MOD_SHIFTSUPER }; class Keybind { diff --git a/src/windowManager.cpp b/src/windowManager.cpp index cf8e70a..bf62da1 100644 --- a/src/windowManager.cpp +++ b/src/windowManager.cpp @@ -101,6 +101,7 @@ void CWindowManager::setupManager() { xcb_flush(DisplayConnection); + // MOD + mouse xcb_grab_button(DisplayConnection, 0, Screen->root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, Screen->root, XCB_NONE, @@ -149,7 +150,7 @@ void CWindowManager::setupManager() { updateBarInfo(); // start its' update thread - //Events::setThread(); + Events::setThread(); Debug::log(LOG, "Bar done."); @@ -183,15 +184,23 @@ bool CWindowManager::handleEvent() { Debug::log(LOG, "Event dispatched MAP"); break; case XCB_BUTTON_PRESS: - Events::eventKeyPress(ev); + Events::eventButtonPress(ev); Debug::log(LOG, "Event dispatched BUTTON_PRESS"); break; + case XCB_BUTTON_RELEASE: + Events::eventButtonRelease(ev); + Debug::log(LOG, "Event dispatched BUTTON_RELEASE"); + break; + case XCB_MOTION_NOTIFY: + Events::eventMotionNotify(ev); + //Debug::log(LOG, "Event dispatched MOTION_NOTIFY"); // Spam!! + break; case XCB_EXPOSE: Events::eventExpose(ev); Debug::log(LOG, "Event dispatched EXPOSE"); break; case XCB_KEY_PRESS: - Events::eventButtonPress(ev); + Events::eventKeyPress(ev); Debug::log(LOG, "Event dispatched KEY_PRESS"); break; @@ -247,13 +256,12 @@ void CWindowManager::cleanupUnusedWorkspaces() { void CWindowManager::refreshDirtyWindows() { for(auto& window : windows) { if (window.getDirty()) { + window.setDirty(false); // Check if the window isn't a node - if (window.getChildNodeAID() != 0) { - window.setDirty(false); + if (window.getChildNodeAID() != 0) continue; - } - + setEffectiveSizePosUsingConfig(&window); // Fullscreen flag @@ -319,8 +327,6 @@ void CWindowManager::refreshDirtyWindows() { xcb_change_window_attributes(DisplayConnection, window.getDrawable(), XCB_CW_BORDER_PIXEL, Values); } - window.setDirty(false); - Debug::log(LOG, "Refreshed dirty window, with an ID of " + std::to_string(window.getDrawable())); } } @@ -337,6 +343,12 @@ void CWindowManager::setFocusedWindow(xcb_drawable_t window) { Values[0] = ConfigManager::getInt("col.active_border"); xcb_change_window_attributes(DisplayConnection, window, XCB_CW_BORDER_PIXEL, Values); + float values[1]; + if (g_pWindowManager->getWindowFromDrawable(window) && g_pWindowManager->getWindowFromDrawable(window)->getIsFloating()) { + values[0] = XCB_STACK_MODE_ABOVE; + xcb_configure_window(g_pWindowManager->DisplayConnection, window, XCB_CONFIG_WINDOW_STACK_MODE, values); + } + LastWindow = window; } } @@ -404,15 +416,7 @@ void CWindowManager::setEffectiveSizePosUsingConfig(CWindow* pWindow) { CWindow* CWindowManager::findWindowAtCursor() { const auto POINTERCOOKIE = xcb_query_pointer(DisplayConnection, Screen->root); - xcb_query_pointer_reply_t* pointerreply = xcb_query_pointer_reply(DisplayConnection, POINTERCOOKIE, NULL); - if (!pointerreply) { - Debug::log(ERR, "Couldn't query pointer."); - return nullptr; - } - - Vector2D cursorPos = Vector2D(pointerreply->root_x, pointerreply->root_y); - - free(pointerreply); + Vector2D cursorPos = getCursorPos(); const auto WORKSPACE = activeWorkspaces[getMonitorFromCursor()->ID]; @@ -473,6 +477,9 @@ void CWindowManager::calculateNewTileSetOldTile(CWindow* pWindow) { + std::to_string(pWindow->getPosition().y) + " " + std::to_string(pWindow->getSize().x) + " " + std::to_string(pWindow->getSize().y)); } + + Values[0] = XCB_STACK_MODE_BELOW; + xcb_configure_window(DisplayConnection, pWindow->getDrawable(), XCB_CONFIG_WINDOW_STACK_MODE, Values); } void CWindowManager::calculateNewFloatingWindow(CWindow* pWindow) { @@ -481,6 +488,9 @@ void CWindowManager::calculateNewFloatingWindow(CWindow* pWindow) { pWindow->setPosition(pWindow->getDefaultPosition()); pWindow->setSize(pWindow->getDefaultSize()); + + Values[0] = XCB_STACK_MODE_ABOVE; + xcb_configure_window(DisplayConnection, pWindow->getDrawable(), XCB_CONFIG_WINDOW_STACK_MODE, Values); } void CWindowManager::calculateNewWindowParams(CWindow* pWindow) { @@ -588,6 +598,11 @@ void CWindowManager::fixWindowOnClose(CWindow* pClosedWindow) { // Get the sibling const auto PSIBLING = getWindowFromDrawable(PPARENT->getChildNodeAID() == pClosedWindow->getDrawable() ? PPARENT->getChildNodeBID() : PPARENT->getChildNodeAID()); + if (!PSIBLING) { + Debug::log(ERR, "No sibling found in fixOnClose! (Corrupted tree...?)"); + return; + } + PSIBLING->setPosition(PPARENT->getPosition()); PSIBLING->setSize(PPARENT->getSize()); @@ -784,16 +799,7 @@ SMonitor* CWindowManager::getMonitorFromWindow(CWindow* pWindow) { } SMonitor* CWindowManager::getMonitorFromCursor() { - const auto POINTERCOOKIE = xcb_query_pointer(DisplayConnection, Screen->root); - - xcb_query_pointer_reply_t* pointerreply = xcb_query_pointer_reply(DisplayConnection, POINTERCOOKIE, NULL); - if (!pointerreply) { - Debug::log(ERR, "Couldn't query pointer."); - return nullptr; - } - - const auto CURSORPOS = Vector2D(pointerreply->root_x, pointerreply->root_y); - free(pointerreply); + const auto CURSORPOS = getCursorPos(); for (auto& monitor : monitors) { if (VECINRECT(CURSORPOS, monitor.vecPosition.x, monitor.vecPosition.y, monitor.vecPosition.x + monitor.vecSize.x, monitor.vecPosition.y + monitor.vecSize.y)) @@ -804,6 +810,21 @@ SMonitor* CWindowManager::getMonitorFromCursor() { return nullptr; } +Vector2D CWindowManager::getCursorPos() { + const auto POINTERCOOKIE = xcb_query_pointer(DisplayConnection, Screen->root); + + xcb_query_pointer_reply_t* pointerreply = xcb_query_pointer_reply(DisplayConnection, POINTERCOOKIE, NULL); + if (!pointerreply) { + Debug::log(ERR, "Couldn't query pointer."); + return Vector2D(0,0); + } + + const auto CURSORPOS = Vector2D(pointerreply->root_x, pointerreply->root_y); + free(pointerreply); + + return CURSORPOS; +} + bool CWindowManager::isWorkspaceVisible(int workspaceID) { for (auto& workspace : activeWorkspaces) { diff --git a/src/windowManager.hpp b/src/windowManager.hpp index 317f50b..31bad4b 100644 --- a/src/windowManager.hpp +++ b/src/windowManager.hpp @@ -24,6 +24,9 @@ public: std::vector monitors; bool modKeyDown = false; + int mouseKeyDown = 0; + Vector2D mouseLastPos = Vector2D(0, 0); + int64_t actingOnWindowFloating = 0; uint8_t Depth = 32; xcb_visualtype_t* VisualType; @@ -64,6 +67,8 @@ public: SMonitor* getMonitorFromWindow(CWindow*); SMonitor* getMonitorFromCursor(); + Vector2D getCursorPos(); + // finds a window that's tiled at cursor. CWindow* findWindowAtCursor();