diff --git a/example/hypr.conf b/example/hypr.conf index dc548f0..c72f243 100644 --- a/example/hypr.conf +++ b/example/hypr.conf @@ -11,6 +11,7 @@ max_fps=60 # max fps for updates of config & animations layout=0 # 0 - dwindle (default), 1 - master focus_when_hover=1 # 0 - do not switch the focus when hover (only for tiling) main_mod=SUPER # For moving, resizing +intelligent_transients=1 # keeps transients always on top. # Execs diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index c2928cd..a3dca83 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -15,6 +15,7 @@ void ConfigManager::init() { configValues["gaps_out"].intValue = 20; configValues["rounding"].intValue = 5; configValues["main_mod"].strValue = "SUPER"; + configValues["intelligent_transients"].intValue = 1; configValues["focus_when_hover"].intValue = 1; diff --git a/src/events/events.cpp b/src/events/events.cpp index 876d647..d7425b6 100644 --- a/src/events/events.cpp +++ b/src/events/events.cpp @@ -303,9 +303,6 @@ CWindow* Events::remapFloatingWindow(int windowID, int forcemonitor) { g_pWindowManager->Values[0] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE; xcb_change_window_attributes_checked(g_pWindowManager->DisplayConnection, windowID, XCB_CW_EVENT_MASK, g_pWindowManager->Values); - // Make all floating windows above - g_pWindowManager->setAllFloatingWindowsTop(); - // Fix docks if (window.getDock()) g_pWindowManager->recalcAllDocks(); @@ -432,9 +429,6 @@ CWindow* Events::remapWindow(int windowID, bool wasfloating, int forcemonitor) { // Focus g_pWindowManager->setFocusedWindow(windowID); - // Make all floating windows above - g_pWindowManager->setAllFloatingWindowsTop(); - return PWINDOWINARR; } @@ -481,6 +475,12 @@ void Events::eventMapWindow(xcb_generic_event_t* event) { // Do ICCCM g_pWindowManager->getICCCMWMProtocols(pNewWindow); + // Do transient checks + EWMH::checkTransient(E->window); + + // Make all floating windows above + g_pWindowManager->setAllFloatingWindowsTop(); + // Set not under pNewWindow->setUnderFullscreen(false); pNewWindow->setDirty(true); diff --git a/src/ewmh/ewmh.cpp b/src/ewmh/ewmh.cpp index c02966c..d868075 100644 --- a/src/ewmh/ewmh.cpp +++ b/src/ewmh/ewmh.cpp @@ -128,4 +128,43 @@ void EWMH::updateWindow(xcb_window_t win) { long data[] = {XCB_ICCCM_WM_STATE_NORMAL, XCB_NONE}; xcb_change_property(g_pWindowManager->DisplayConnection, XCB_PROP_MODE_REPLACE, win, HYPRATOMS["WM_STATE"], HYPRATOMS["WM_STATE"], 32, 2, data); } +} + +void EWMH::checkTransient(xcb_window_t window) { + + const auto PWINDOW = g_pWindowManager->getWindowFromDrawable(window); + + if (!PWINDOW) + return; + + // Check if it's a transient + const auto TRANSIENTCOOKIE = xcb_get_property(g_pWindowManager->DisplayConnection, false, window, 68 /* TRANSIENT_FOR */, XCB_GET_PROPERTY_TYPE_ANY, 0, UINT32_MAX); + const auto TRANSIENTREPLY = xcb_get_property_reply(g_pWindowManager->DisplayConnection, TRANSIENTCOOKIE, NULL); + + if (!TRANSIENTREPLY || xcb_get_property_value_length(TRANSIENTREPLY) == 0) { + Debug::log(WARN, "Transient check failed."); + return; + } + + xcb_window_t transientWindow; + if (!xcb_icccm_get_wm_transient_for_from_reply(&transientWindow, TRANSIENTREPLY)) { + Debug::log(WARN, "Transient reply failed."); + free(TRANSIENTREPLY); + return; + } + + // set the flags + const auto PPARENTWINDOW = g_pWindowManager->getWindowFromDrawable(transientWindow); + + if (!PPARENTWINDOW) { + free(TRANSIENTREPLY); + Debug::log(LOG, "Transient set for a nonexistent window, ignoring."); + return; + } + + PPARENTWINDOW->addTransientChild(window); + + Debug::log(LOG, "Added a transient child to " + std::to_string(transientWindow) + "."); + + free(TRANSIENTREPLY); } \ No newline at end of file diff --git a/src/ewmh/ewmh.hpp b/src/ewmh/ewmh.hpp index d72310f..5b31e2d 100644 --- a/src/ewmh/ewmh.hpp +++ b/src/ewmh/ewmh.hpp @@ -10,6 +10,7 @@ namespace EWMH { void setFrameExtents(xcb_window_t); void refreshAllExtents(); void updateDesktops(); + void checkTransient(xcb_window_t); namespace DesktopInfo { inline int lastid = 0; diff --git a/src/window.cpp b/src/window.cpp index f4a29ed..4a899b2 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,7 +1,7 @@ #include "window.hpp" #include "windowManager.hpp" -CWindow::CWindow() { this->setLastUpdatePosition(Vector2D(0,0)); this->setLastUpdateSize(Vector2D(0,0)); this->setDock(false); this->setUnderFullscreen(false); this->setIsSleeping(true); this->setFirstAnimFrame(true); this->setIsAnimated(false); this->setDead(false); this->setMasterChildIndex(0); this->setMaster(false); this->setCanKill(false); this->setImmovable(false); this->setNoInterventions(false); this->setDirty(true); this->setFullscreen(false); this->setIsFloating(false); this->setParentNodeID(0); this->setChildNodeAID(0); this->setChildNodeBID(0); this->setName(""); } +CWindow::CWindow() { this->setTransient(false); this->setLastUpdatePosition(Vector2D(0,0)); this->setLastUpdateSize(Vector2D(0,0)); this->setDock(false); this->setUnderFullscreen(false); this->setIsSleeping(true); this->setFirstAnimFrame(true); this->setIsAnimated(false); this->setDead(false); this->setMasterChildIndex(0); this->setMaster(false); this->setCanKill(false); this->setImmovable(false); this->setNoInterventions(false); this->setDirty(true); this->setFullscreen(false); this->setIsFloating(false); this->setParentNodeID(0); this->setChildNodeAID(0); this->setChildNodeBID(0); this->setName(""); } CWindow::~CWindow() { } void CWindow::generateNodeID() { @@ -45,4 +45,30 @@ void CWindow::recalcSizePosRecursive() { g_pWindowManager->getWindowFromDrawable(m_iChildNodeBID)->recalcSizePosRecursive(); } } +} + +void CWindow::bringTopRecursiveTransients() { + // check if its enabled + if (ConfigManager::getInt("intelligent_transients") != 1) + return; + + // first top all the children if floating + for (auto& c : m_vecChildren) { + if (const auto PWINDOW = g_pWindowManager->getWindowFromDrawable(c); PWINDOW) { + if (PWINDOW->getIsFloating()) + g_pWindowManager->setAWindowTop(c); + } + } + + // THEN top their children + for (auto& c : m_vecChildren) { + if (const auto PCHILD = g_pWindowManager->getWindowFromDrawable(c); PCHILD) { + // recurse + PCHILD->bringTopRecursiveTransients(); + } + } +} + +void CWindow::addTransientChild(xcb_window_t w) { + m_vecChildren.push_back(w); } \ No newline at end of file diff --git a/src/window.hpp b/src/window.hpp index 39a4fb6..8e9b238 100644 --- a/src/window.hpp +++ b/src/window.hpp @@ -39,7 +39,9 @@ public: // Tells the window manager to reload the window's params EXPOSED_MEMBER(Dirty, bool, b); + void bringTopRecursiveTransients(); void setDirtyRecursive(bool); + void addTransientChild(xcb_drawable_t); // ONLY for dwindle layout! void recalcSizePosRecursive(); @@ -95,7 +97,9 @@ public: EXPOSED_MEMBER(Dock, bool, b); EXPOSED_MEMBER(DockAlign, EDockAlign, e); - // todo: Transients + // Transient + EXPOSED_MEMBER(Children, std::vector, vec); + EXPOSED_MEMBER(Transient, bool, b); private: diff --git a/src/windowManager.cpp b/src/windowManager.cpp index a4ad193..3adbff1 100644 --- a/src/windowManager.cpp +++ b/src/windowManager.cpp @@ -460,11 +460,6 @@ void CWindowManager::setFocusedWindow(xcb_drawable_t window) { float values[1]; if (const auto PWINDOW = g_pWindowManager->getWindowFromDrawable(window); PWINDOW) { - if (PWINDOW->getIsFloating()) { - values[0] = XCB_STACK_MODE_ABOVE; - xcb_configure_window(g_pWindowManager->DisplayConnection, window, XCB_CONFIG_WINDOW_STACK_MODE, values); - } - // Apply rounded corners, does all the checks inside. // The border changed so let's not make it rectangular maybe applyShapeToWindow(PWINDOW); @@ -472,9 +467,15 @@ void CWindowManager::setFocusedWindow(xcb_drawable_t window) { LastWindow = window; - if (g_pWindowManager->getWindowFromDrawable(window)) + const auto PNEWFOCUS = g_pWindowManager->getWindowFromDrawable(window); + + if (PNEWFOCUS) { applyShapeToWindow(g_pWindowManager->getWindowFromDrawable(window)); + // Transients + PNEWFOCUS->bringTopRecursiveTransients(); + } + // set focus in X11 xcb_set_input_focus(DisplayConnection, XCB_INPUT_FOCUS_POINTER_ROOT, window, XCB_CURRENT_TIME); @@ -1557,9 +1558,13 @@ void CWindowManager::updateBarInfo() { void CWindowManager::setAllFloatingWindowsTop() { for (auto& window : windows) { - if (window.getIsFloating()) { + if (window.getIsFloating() && !window.getTransient()) { Values[0] = XCB_STACK_MODE_ABOVE; xcb_configure_window(g_pWindowManager->DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_STACK_MODE, Values); + + window.bringTopRecursiveTransients(); + } else if (window.getChildren().size() > 0) { + window.bringTopRecursiveTransients(); } } }