diff --git a/CMakeLists.txt b/CMakeLists.txt index 608ccc62..80483c4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,7 +110,7 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo pango pangocairo pixman-1 libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm - hyprwayland-scanner>=0.3.6 hyprlang>=0.3.2 hyprcursor>=0.1.7 + hyprwayland-scanner>=0.3.7 hyprlang>=0.3.2 hyprcursor>=0.1.7 ) file(GLOB_RECURSE SRCFILES "src/*.cpp") @@ -260,7 +260,6 @@ target_link_libraries(Hyprland protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true) protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true) protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml" "hyprland-toplevel-export-v1" true) -protocol("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false) protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false) protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false) @@ -292,6 +291,7 @@ protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1 protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false) protocolNew("stable/tablet/tablet-v2.xml" "tablet-v2" false) protocolNew("stable/presentation-time/presentation-time.xml" "presentation-time" false) +protocolNew("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false) protocolWayland() diff --git a/protocols/meson.build b/protocols/meson.build index 95c9fec4..4b7aa200 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -61,6 +61,7 @@ new_protocols = [ [wl_protocol_dir, 'staging/ext-session-lock/ext-session-lock-v1.xml'], [wl_protocol_dir, 'stable/tablet/tablet-v2.xml'], [wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'], + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], ] wl_protos_src = [] diff --git a/src/Compositor.cpp b/src/Compositor.cpp index efc02a5f..c56dd280 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -18,6 +18,7 @@ #include "protocols/FractionalScale.hpp" #include "protocols/PointerConstraints.hpp" #include "protocols/LayerShell.hpp" +#include "protocols/XDGShell.hpp" #include "desktop/LayerSurface.hpp" #include @@ -229,8 +230,6 @@ void CCompositor::initServer() { // wlr_primary_selection_v1_device_manager_create(m_sWLDisplay); wlr_viewporter_create(m_sWLDisplay); - m_sWLRXDGShell = wlr_xdg_shell_create(m_sWLDisplay, 6); - m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend); if (!m_sWRLDRMLeaseMgr) { Debug::log(INFO, "Failed to create wlr_drm_lease_v1_manager"); @@ -253,7 +252,6 @@ void CCompositor::initServer() { void CCompositor::initAllSignals() { addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend"); - addWLSignal(&m_sWLRXDGShell->events.new_toplevel, &Events::listen_newXDGToplevel, m_sWLRXDGShell, "XDG Shell"); addWLSignal(&m_sWLRBackend->events.new_input, &Events::listen_newInput, m_sWLRBackend, "Backend"); // addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat"); // addWLSignal(&m_sSeat.seat->events.request_start_drag, &Events::listen_requestDrag, &m_sSeat, "Seat"); @@ -271,7 +269,6 @@ void CCompositor::initAllSignals() { void CCompositor::removeAllSignals() { removeWLSignal(&Events::listen_newOutput); - removeWLSignal(&Events::listen_newXDGToplevel); removeWLSignal(&Events::listen_newInput); removeWLSignal(&Events::listen_requestSetSel); removeWLSignal(&Events::listen_requestDrag); @@ -805,22 +802,28 @@ wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, PHLWINDOW p RASSERT(!pWindow->m_bIsX11, "Cannot call vectorWindowToSurface on an X11 window!"); - const auto PSURFACE = pWindow->m_uSurface.xdg; + double subx, suby; - double subx, suby; + CBox geom = pWindow->m_pXDGSurface->current.geometry; - // calc for oversized windows... fucking bullshit, again. - CBox geom; - wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, geom.pWlr()); - geom.applyFromWlr(); + // try popups first + const auto PPOPUP = pWindow->m_pPopupHead->at(pos); - const auto PFOUND = - wlr_xdg_surface_surface_at(PSURFACE, pos.x - pWindow->m_vRealPosition.value().x + geom.x, pos.y - pWindow->m_vRealPosition.value().y + geom.y, &subx, &suby); + wlr_surface* found = PPOPUP ? PPOPUP->m_sWLSurface.wlr() : nullptr; - if (PFOUND) { + if (!PPOPUP) + found = wlr_surface_surface_at(pWindow->m_pWLSurface.wlr(), pos.x - pWindow->m_vRealPosition.value().x + geom.x, pos.y - pWindow->m_vRealPosition.value().y + geom.y, &subx, + &suby); + else { + const auto OFF = PPOPUP->coordsRelativeToParent(); + subx = pos.x - OFF.x + geom.x - pWindow->m_vRealPosition.goal().x; + suby = pos.y - OFF.y + geom.y - pWindow->m_vRealPosition.goal().y; + } + + if (found) { sl.x = subx; sl.y = suby; - return PFOUND; + return found; } sl.x = pos.x - pWindow->m_vRealPosition.value().x; @@ -829,7 +832,7 @@ wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, PHLWINDOW p sl.x += geom.x; sl.y += geom.y; - return PSURFACE->surface; + return pWindow->m_pWLSurface.wlr(); } Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindow, wlr_surface* pSurface) { @@ -839,12 +842,14 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo if (pWindow->m_bIsX11) return vec - pWindow->m_vRealPosition.goal(); - const auto PSURFACE = pWindow->m_uSurface.xdg; + const auto PPOPUP = pWindow->m_pPopupHead->at(vec); + if (PPOPUP) + return vec - PPOPUP->coordsGlobal(); std::tuple iterData = {pSurface, -1337, -1337}; - wlr_xdg_surface_for_each_surface( - PSURFACE, + wlr_surface_for_each_surface( + pWindow->m_pWLSurface.wlr(), [](wlr_surface* surf, int x, int y, void* data) { const auto PDATA = (std::tuple*)data; if (surf == std::get<0>(*PDATA)) { @@ -854,9 +859,7 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo }, &iterData); - CBox geom = {}; - wlr_xdg_surface_get_geometry(PSURFACE, geom.pWlr()); - geom.applyFromWlr(); + CBox geom = pWindow->m_pXDGSurface->current.geometry; if (std::get<1>(iterData) == -1337 && std::get<2>(iterData) == -1337) return vec - pWindow->m_vRealPosition.goal(); @@ -993,7 +996,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { pWindow->m_bIsUrgent = false; // Send an event - g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(pWindow) + "," + pWindow->m_szTitle}); + g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", pWindow->m_szClass + "," + pWindow->m_szTitle}); g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)pWindow.get())}); EMIT_HOOK_EVENT("activeWindow", pWindow); @@ -2310,7 +2313,7 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) { switch (mode) { case MODE_CLASS_REGEX: { - const auto windowClass = g_pXWaylandManager->getAppIDClass(w); + const auto windowClass = w->m_szClass; if (!std::regex_search(windowClass, regexCheck)) continue; break; @@ -2322,7 +2325,7 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) { break; } case MODE_TITLE_REGEX: { - const auto windowTitle = g_pXWaylandManager->getTitle(w); + const auto windowTitle = w->m_szTitle; if (!std::regex_search(windowTitle, regexCheck)) continue; break; diff --git a/src/Compositor.hpp b/src/Compositor.hpp index eedcea56..574889bc 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -50,7 +50,6 @@ class CCompositor { wlr_subcompositor* m_sWLRSubCompositor; wlr_drm* m_sWRLDRM; wlr_drm_lease_v1_manager* m_sWRLDRMLeaseMgr; - wlr_xdg_shell* m_sWLRXDGShell; wlr_egl* m_sWLREGL; int m_iDRMFD; wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 4a75ca1d..15d5ae81 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -1042,8 +1042,8 @@ std::vector CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo std::vector returns; - std::string title = g_pXWaylandManager->getTitle(pWindow); - std::string appidclass = g_pXWaylandManager->getAppIDClass(pWindow); + std::string title = pWindow->m_szTitle; + std::string appidclass = pWindow->m_szClass; Debug::log(LOG, "Searching for matching rules for {} (title: {})", appidclass, title); diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index f8fdb03f..51071533 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -210,10 +210,10 @@ static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { (uintptr_t)w.get(), (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition.goal().x, (int)w->m_vRealPosition.goal().y, (int)w->m_vRealSize.goal().x, (int)w->m_vRealSize.goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, escapeJSONStrings(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), ((int)w->m_bIsFloating == 1 ? "true" : "false"), (int64_t)w->m_iMonitorID, - escapeJSONStrings(g_pXWaylandManager->getAppIDClass(w)), escapeJSONStrings(g_pXWaylandManager->getTitle(w)), escapeJSONStrings(w->m_szInitialClass), - escapeJSONStrings(w->m_szInitialTitle), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), - (w->m_bIsFullscreen ? "true" : "false"), (w->m_bIsFullscreen ? (w->m_pWorkspace ? (int)w->m_pWorkspace->m_efFullscreenMode : 0) : 0), - w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); + escapeJSONStrings(w->m_szClass), escapeJSONStrings(w->m_szTitle), escapeJSONStrings(w->m_szInitialClass), escapeJSONStrings(w->m_szInitialTitle), w->getPID(), + ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), (w->m_bIsFullscreen ? "true" : "false"), + (w->m_bIsFullscreen ? (w->m_pWorkspace ? (int)w->m_pWorkspace->m_efFullscreenMode : 0) : 0), w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), + (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); } else { return std::format("Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: " "{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: " @@ -221,8 +221,8 @@ static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { "{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n", (uintptr_t)w.get(), w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goal().x, (int)w->m_vRealPosition.goal().y, (int)w->m_vRealSize.goal().x, (int)w->m_vRealSize.goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, - (!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), (int)w->m_bIsFloating, (int64_t)w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w), - g_pXWaylandManager->getTitle(w), w->m_szInitialClass, w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen, + (!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), (int)w->m_bIsFloating, (int64_t)w->m_iMonitorID, w->m_szClass, w->m_szTitle, w->m_szInitialClass, + w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen, (w->m_bIsFullscreen ? (w->m_pWorkspace ? w->m_pWorkspace->m_efFullscreenMode : 0) : 0), (int)w->m_bFakeFullscreenState, getGroupedData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); } diff --git a/src/desktop/Popup.cpp b/src/desktop/Popup.cpp index e172744d..605dfdfd 100644 --- a/src/desktop/Popup.cpp +++ b/src/desktop/Popup.cpp @@ -2,6 +2,7 @@ #include "../config/ConfigValue.hpp" #include "../Compositor.hpp" #include "../protocols/LayerShell.hpp" +#include "../protocols/XDGShell.hpp" #include CPopup::CPopup(PHLWINDOW pOwner) : m_pWindowOwner(pOwner) { @@ -12,14 +13,13 @@ CPopup::CPopup(PHLLS pOwner) : m_pLayerOwner(pOwner) { initAllSignals(); } -CPopup::CPopup(wlr_xdg_popup* popup, CPopup* pOwner) : m_pParent(pOwner), m_pWLR(popup) { - m_pWLR->base->data = this; - m_sWLSurface.assign(popup->base->surface, this); +CPopup::CPopup(SP popup, CPopup* pOwner) : m_pParent(pOwner), m_pResource(popup) { + m_sWLSurface.assign(popup->surface->surface, this); m_pLayerOwner = pOwner->m_pLayerOwner; m_pWindowOwner = pOwner->m_pWindowOwner; - m_vLastSize = {m_pWLR->current.geometry.width, m_pWLR->current.geometry.height}; + m_vLastSize = popup->surface->current.geometry.size(); unconstrain(); initAllSignals(); @@ -27,71 +27,32 @@ CPopup::CPopup(wlr_xdg_popup* popup, CPopup* pOwner) : m_pParent(pOwner), m_pWLR CPopup::~CPopup() { m_sWLSurface.unassign(); - if (m_pWLR) - m_pWLR->base->data = nullptr; - - hyprListener_commitPopup.removeCallback(); - hyprListener_repositionPopup.removeCallback(); - hyprListener_mapPopup.removeCallback(); - hyprListener_unmapPopup.removeCallback(); - hyprListener_newPopup.removeCallback(); - hyprListener_destroyPopup.removeCallback(); -} - -static void onNewPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onNewPopup((wlr_xdg_popup*)data); -} - -static void onMapPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onMap(); -} - -static void onDestroyPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onDestroy(); -} - -static void onUnmapPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onUnmap(); -} - -static void onCommitPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onCommit(); -} - -static void onRepositionPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onReposition(); } void CPopup::initAllSignals() { - if (!m_pWLR) { + if (!m_pResource) { if (!m_pWindowOwner.expired()) - hyprListener_newPopup.initCallback(&m_pWindowOwner->m_uSurface.xdg->events.new_popup, ::onNewPopup, this, "CPopup Head"); + listeners.newPopup = m_pWindowOwner->m_pXDGSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast>(d)); }); else if (!m_pLayerOwner.expired()) - listeners.newPopup = m_pLayerOwner->layerSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast(d)); }); + listeners.newPopup = m_pLayerOwner->layerSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast>(d)); }); else ASSERT(false); return; } - hyprListener_repositionPopup.initCallback(&m_pWLR->events.reposition, ::onRepositionPopup, this, "CPopup"); - hyprListener_destroyPopup.initCallback(&m_pWLR->events.destroy, ::onDestroyPopup, this, "CPopup"); - hyprListener_mapPopup.initCallback(&m_sWLSurface.wlr()->events.map, ::onMapPopup, this, "CPopup"); - hyprListener_unmapPopup.initCallback(&m_sWLSurface.wlr()->events.unmap, ::onUnmapPopup, this, "CPopup"); - hyprListener_commitPopup.initCallback(&m_sWLSurface.wlr()->events.commit, ::onCommitPopup, this, "CPopup"); - hyprListener_newPopup.initCallback(&m_pWLR->base->events.new_popup, ::onNewPopup, this, "CPopup"); + listeners.reposition = m_pResource->events.reposition.registerListener([this](std::any d) { this->onReposition(); }); + listeners.map = m_pResource->surface->events.map.registerListener([this](std::any d) { this->onMap(); }); + listeners.unmap = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); }); + listeners.destroy = m_pResource->surface->events.destroy.registerListener([this](std::any d) { this->onDestroy(); }); + listeners.commit = m_pResource->surface->events.commit.registerListener([this](std::any d) { this->onCommit(); }); + listeners.newPopup = m_pResource->surface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast>(d)); }); } -void CPopup::onNewPopup(wlr_xdg_popup* popup) { +void CPopup::onNewPopup(SP popup) { const auto POPUP = m_vChildren.emplace_back(std::make_unique(popup, this)).get(); - Debug::log(LOG, "New popup at wlr {:x} and hl {:x}", (uintptr_t)popup, (uintptr_t)POPUP); + Debug::log(LOG, "New popup at {:x}", (uintptr_t)POPUP); } void CPopup::onDestroy() { @@ -104,7 +65,7 @@ void CPopup::onDestroy() { } void CPopup::onMap() { - m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height}; + m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; const auto COORDS = coordsGlobal(); CBox box; @@ -126,7 +87,9 @@ void CPopup::onMap() { } void CPopup::onUnmap() { - m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height}; + if (!m_pResource || !m_pResource->surface) + return; + m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; const auto COORDS = coordsGlobal(); CBox box; @@ -140,16 +103,27 @@ void CPopup::onUnmap() { if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP) g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer)); + + // damage all children + breadthfirst( + [this](CPopup* p, void* data) { + if (!p->m_pResource) + return; + + auto box = CBox{p->coordsGlobal(), p->size()}; + g_pHyprRenderer->damageBox(&box); + }, + nullptr); } void CPopup::onCommit(bool ignoreSiblings) { - if (m_pWLR->base->initial_commit) { - wlr_xdg_surface_schedule_configure(m_pWLR->base); + if (m_pResource->surface->initialCommit) { + m_pResource->surface->scheduleConfigure(); return; } if (!m_pWindowOwner.expired() && (!m_pWindowOwner->m_bIsMapped || !m_pWindowOwner->m_pWorkspace->m_bVisible)) { - m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height}; + m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); if (*PLOGDAMAGE) @@ -157,16 +131,17 @@ void CPopup::onCommit(bool ignoreSiblings) { return; } - if (!m_pWLR->base->surface->mapped) + if (!m_pResource->surface->mapped) return; const auto COORDS = coordsGlobal(); const auto COORDSLOCAL = coordsRelativeToParent(); - if (m_vLastSize != Vector2D{m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height} || m_bRequestedReposition || m_vLastPos != COORDSLOCAL) { + if (m_vLastSize != Vector2D{m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height} || m_bRequestedReposition || + m_vLastPos != COORDSLOCAL) { CBox box = {localToGlobal(m_vLastPos), m_vLastSize}; g_pHyprRenderer->damageBox(&box); - m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height}; + m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; box = {COORDS, m_vLastSize}; g_pHyprRenderer->damageBox(&box); @@ -202,20 +177,25 @@ void CPopup::unconstrain() { return; CBox box = {PMONITOR->vecPosition.x - COORDS.x, PMONITOR->vecPosition.y - COORDS.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; - wlr_xdg_popup_unconstrain_from_box(m_pWLR, box.pWlr()); + m_pResource->applyPositioning(box, COORDS - PMONITOR->vecPosition); + wlr_surface_send_enter(m_pResource->surface->surface, PMONITOR->output); } Vector2D CPopup::coordsRelativeToParent() { Vector2D offset; - CPopup* current = this; + if (!m_pResource) + return {}; - offset -= {m_pWLR->base->current.geometry.x, m_pWLR->base->current.geometry.y}; + CPopup* current = this; + offset -= current->m_pResource->surface->current.geometry.pos(); - while (current->m_pParent) { + offset -= m_pResource->surface->current.geometry.pos(); + + while (current->m_pParent && current->m_pResource) { offset += {current->m_sWLSurface.wlr()->current.dx, current->m_sWLSurface.wlr()->current.dy}; - offset += {current->m_pWLR->current.geometry.x, current->m_pWLR->current.geometry.y}; + offset += current->m_pResource->geometry.pos(); current = current->m_pParent; } @@ -309,7 +289,7 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) { breadthfirst([](CPopup* popup, void* data) { ((std::vector*)data)->push_back(popup); }, &popups); for (auto& p : popups | std::views::reverse) { - if (!p->m_pWLR) + if (!p->m_pResource) continue; if (!allowsInput) { diff --git a/src/desktop/Popup.hpp b/src/desktop/Popup.hpp index 6aa7ce61..e6c35e68 100644 --- a/src/desktop/Popup.hpp +++ b/src/desktop/Popup.hpp @@ -5,6 +5,8 @@ #include "Subsurface.hpp" #include "../helpers/signal/Listener.hpp" +class CXDGPopupResource; + class CPopup { public: // dummy head nodes @@ -12,7 +14,7 @@ class CPopup { CPopup(PHLLS pOwner); // real nodes - CPopup(wlr_xdg_popup* popup, CPopup* pOwner); + CPopup(SP popup, CPopup* pOwner); ~CPopup(); @@ -21,7 +23,7 @@ class CPopup { Vector2D size(); - void onNewPopup(wlr_xdg_popup* popup); + void onNewPopup(SP popup); void onDestroy(); void onMap(); void onUnmap(); @@ -45,31 +47,28 @@ class CPopup { PHLLSREF m_pLayerOwner; // T2 owners - CPopup* m_pParent = nullptr; + CPopup* m_pParent = nullptr; - wlr_xdg_popup* m_pWLR = nullptr; + WP m_pResource; - Vector2D m_vLastSize = {}; - Vector2D m_vLastPos = {}; + Vector2D m_vLastSize = {}; + Vector2D m_vLastPos = {}; - bool m_bRequestedReposition = false; + bool m_bRequestedReposition = false; - bool m_bInert = false; + bool m_bInert = false; // std::vector> m_vChildren; std::unique_ptr m_pSubsurfaceHead; - // signals - DYNLISTENER(newPopup); - DYNLISTENER(destroyPopup); - DYNLISTENER(mapPopup); - DYNLISTENER(unmapPopup); - DYNLISTENER(commitPopup); - DYNLISTENER(repositionPopup); - struct { CHyprSignalListener newPopup; + CHyprSignalListener destroy; + CHyprSignalListener map; + CHyprSignalListener unmap; + CHyprSignalListener commit; + CHyprSignalListener reposition; } listeners; void initAllSignals(); diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index e87bd99c..c11a714b 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -6,6 +6,7 @@ #include "../config/ConfigValue.hpp" #include #include "../managers/TokenManager.hpp" +#include "../protocols/XDGShell.hpp" PHLWINDOW CWindow::create() { PHLWINDOW pWindow = SP(new CWindow); @@ -27,6 +28,39 @@ PHLWINDOW CWindow::create() { return pWindow; } +PHLWINDOW CWindow::create(SP resource) { + PHLWINDOW pWindow = SP(new CWindow(resource)); + + pWindow->m_pSelf = pWindow; + resource->toplevel->window = pWindow; + + pWindow->m_vRealPosition.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); + pWindow->m_vRealSize.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); + pWindow->m_fBorderFadeAnimationProgress.create(g_pConfigManager->getAnimationPropertyConfig("border"), pWindow, AVARDAMAGE_BORDER); + pWindow->m_fBorderAngleAnimationProgress.create(g_pConfigManager->getAnimationPropertyConfig("borderangle"), pWindow, AVARDAMAGE_BORDER); + pWindow->m_fAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); + pWindow->m_fActiveInactiveAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE); + pWindow->m_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW); + pWindow->m_fDimPercent.create(g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE); + + pWindow->addWindowDeco(std::make_unique(pWindow)); + pWindow->addWindowDeco(std::make_unique(pWindow)); + + pWindow->m_pWLSurface.assign(pWindow->m_pXDGSurface->surface, pWindow); + + return pWindow; +} + +CWindow::CWindow(SP resource) : m_pXDGSurface(resource) { + listeners.map = m_pXDGSurface->events.map.registerListener([this](std::any d) { Events::listener_mapWindow(this, nullptr); }); + listeners.ack = m_pXDGSurface->events.ack.registerListener([this](std::any d) { onAck(std::any_cast(d)); }); + listeners.unmap = m_pXDGSurface->events.unmap.registerListener([this](std::any d) { Events::listener_unmapWindow(this, nullptr); }); + listeners.destroy = m_pXDGSurface->events.destroy.registerListener([this](std::any d) { Events::listener_destroyWindow(this, nullptr); }); + listeners.commit = m_pXDGSurface->events.commit.registerListener([this](std::any d) { Events::listener_commitWindow(this, nullptr); }); + listeners.updateState = m_pXDGSurface->toplevel->events.stateChanged.registerListener([this](std::any d) { onUpdateState(); }); + listeners.updateMetadata = m_pXDGSurface->toplevel->events.metadataChanged.registerListener([this](std::any d) { onUpdateMeta(); }); +} + CWindow::CWindow() { ; } @@ -74,21 +108,24 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() { if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y) maxExtents.bottomRight.y = EXTENTS.bottomRight.y; - if (m_pWLSurface.exists() && !m_bIsX11) { + if (m_pWLSurface.exists() && !m_bIsX11 && m_pPopupHead) { CBox surfaceExtents = {0, 0, 0, 0}; // TODO: this could be better, perhaps make a getFullWindowRegion? - wlr_xdg_surface_for_each_popup_surface( - m_uSurface.xdg, - [](wlr_surface* surf, int sx, int sy, void* data) { + m_pPopupHead->breadthfirst( + [](CPopup* popup, void* data) { + if (!popup->m_sWLSurface.wlr()) + return; + CBox* pSurfaceExtents = (CBox*)data; - if (sx < pSurfaceExtents->x) - pSurfaceExtents->x = sx; - if (sy < pSurfaceExtents->y) - pSurfaceExtents->y = sy; - if (sx + surf->current.width > pSurfaceExtents->width) - pSurfaceExtents->width = sx + surf->current.width - pSurfaceExtents->x; - if (sy + surf->current.height > pSurfaceExtents->height) - pSurfaceExtents->height = sy + surf->current.height - pSurfaceExtents->y; + CBox surf = CBox{popup->coordsRelativeToParent(), popup->size()}; + if (surf.x < pSurfaceExtents->x) + pSurfaceExtents->x = surf.x; + if (surf.y < pSurfaceExtents->y) + pSurfaceExtents->y = surf.y; + if (surf.x + surf.w > pSurfaceExtents->width) + pSurfaceExtents->width = surf.x + surf.w - pSurfaceExtents->x; + if (surf.y + surf.h > pSurfaceExtents->height) + pSurfaceExtents->height = surf.y + surf.h - pSurfaceExtents->y; }, &surfaceExtents); @@ -258,10 +295,10 @@ bool CWindow::checkInputOnDecos(const eInputType type, const Vector2D& mouseCoor pid_t CWindow::getPID() { pid_t PID = -1; if (!m_bIsX11) { - if (!m_uSurface.xdg) + if (!m_pXDGSurface || !m_pXDGSurface->owner /* happens at unmap */) return -1; - wl_client_get_credentials(wl_resource_get_client(m_uSurface.xdg->resource), &PID, nullptr, nullptr); + wl_client_get_credentials(m_pXDGSurface->owner->client(), &PID, nullptr, nullptr); } else { if (!m_uSurface.xwayland) return -1; @@ -511,8 +548,8 @@ void CWindow::onMap() { g_pCompositor->m_vWindowFocusHistory.push_back(m_pSelf); - hyprListener_unmapWindow.initCallback(m_bIsX11 ? &m_uSurface.xwayland->surface->events.unmap : &m_uSurface.xdg->surface->events.unmap, &Events::listener_unmapWindow, this, - "CWindow"); + if (m_bIsX11) + hyprListener_unmapWindow.initCallback(&m_uSurface.xwayland->surface->events.unmap, &Events::listener_unmapWindow, this, "CWindow"); m_vReportedSize = m_vPendingReportedSize; m_bAnimatingIn = true; @@ -793,26 +830,14 @@ bool CWindow::isInCurvedCorner(double x, double y) { return false; } -void findExtensionForVector2D(wlr_surface* surface, int x, int y, void* data) { - const auto DATA = (SExtensionFindingData*)data; - - CBox box = {DATA->origin.x + x, DATA->origin.y + y, surface->current.width, surface->current.height}; - - if (box.containsPoint(DATA->vec)) - *DATA->found = surface; -} - // checks if the wayland window has a popup at pos bool CWindow::hasPopupAt(const Vector2D& pos) { if (m_bIsX11) return false; - wlr_surface* resultSurf = nullptr; - Vector2D origin = m_vRealPosition.value(); - SExtensionFindingData data = {origin, pos, &resultSurf}; - wlr_xdg_surface_for_each_popup_surface(m_uSurface.xdg, findExtensionForVector2D, &data); + CPopup* popup = m_pPopupHead->at(pos); - return resultSurf; + return popup && popup->m_sWLSurface.wlr(); } void CWindow::applyGroupRules() { @@ -1096,11 +1121,11 @@ bool CWindow::opaque() { if (m_bIsX11) return !m_uSurface.xwayland->has_alpha; - if (m_uSurface.xdg->surface->opaque) + if (m_pXDGSurface->surface->opaque) return true; - const auto EXTENTS = pixman_region32_extents(&m_uSurface.xdg->surface->opaque_region); - if (EXTENTS->x2 - EXTENTS->x1 >= m_uSurface.xdg->surface->current.buffer_width && EXTENTS->y2 - EXTENTS->y1 >= m_uSurface.xdg->surface->current.buffer_height) + const auto EXTENTS = pixman_region32_extents(&m_pXDGSurface->surface->opaque_region); + if (EXTENTS->x2 - EXTENTS->x1 >= m_pXDGSurface->surface->current.buffer_width && EXTENTS->y2 - EXTENTS->y1 >= m_pXDGSurface->surface->current.buffer_height) return true; return false; @@ -1162,10 +1187,10 @@ void CWindow::setSuspended(bool suspend) { if (suspend == m_bSuspended) return; - if (m_bIsX11) + if (m_bIsX11 || !m_pXDGSurface->toplevel) return; - wlr_xdg_toplevel_set_suspended(m_uSurface.xdg->toplevel, suspend); + m_pXDGSurface->toplevel->setSuspeneded(suspend); m_bSuspended = suspend; } @@ -1221,12 +1246,21 @@ void CWindow::onWorkspaceAnimUpdate() { } int CWindow::popupsCount() { + if (m_bIsX11) + return 0; + + int no = -1; + m_pPopupHead->breadthfirst([](CPopup* p, void* d) { *((int*)d) += 1; }, &no); + return no; +} + +int CWindow::surfacesCount() { if (m_bIsX11) return 1; int no = 0; - wlr_xdg_surface_for_each_popup_surface( - m_uSurface.xdg, [](wlr_surface* s, int x, int y, void* data) { *(int*)data += 1; }, &no); + wlr_surface_for_each_surface( + m_pWLSurface.wlr(), [](wlr_surface* surf, int x, int y, void* data) { *((int*)data) += 1; }, &no); return no; } @@ -1278,6 +1312,9 @@ std::unordered_map CWindow::getEnv() { } void CWindow::activate(bool force) { + if (g_pCompositor->m_pLastWindow == m_pSelf) + return; + static auto PFOCUSONACTIVATE = CConfigValue("misc:focus_on_activate"); g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)this)}); @@ -1295,3 +1332,98 @@ void CWindow::activate(bool force) { g_pCompositor->focusWindow(m_pSelf.lock()); g_pCompositor->warpCursorTo(middle()); } + +void CWindow::onUpdateState() { + if (!m_pXDGSurface) + return; + + if (m_pXDGSurface->toplevel->state.requestsFullscreen) { + bool fs = m_pXDGSurface->toplevel->state.requestsFullscreen.value(); + + if (fs != m_bIsFullscreen && m_pXDGSurface->mapped) + g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_FULL); + + if (!m_pXDGSurface->mapped) + m_bWantsInitialFullscreen = fs; + } + + if (m_pXDGSurface->toplevel->state.requestsMaximize) { + bool fs = m_pXDGSurface->toplevel->state.requestsMaximize.value(); + + if (fs != m_bIsFullscreen && m_pXDGSurface->mapped) + g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_MAXIMIZED); + } +} + +void CWindow::onUpdateMeta() { + const auto NEWTITLE = fetchTitle(); + + if (m_szTitle != NEWTITLE) { + m_szTitle = NEWTITLE; + g_pEventManager->postEvent(SHyprIPCEvent{"windowtitle", std::format("{:x}", (uintptr_t)this)}); + EMIT_HOOK_EVENT("windowTitle", m_pSelf.lock()); + + if (m_pSelf == g_pCompositor->m_pLastWindow) { // if it's the active, let's post an event to update others + g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", m_szClass + "," + m_szTitle}); + g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)this)}); + EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock()); + } + + updateDynamicRules(); + g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock()); + updateToplevel(); + + Debug::log(LOG, "Window {:x} set title to {}", (uintptr_t)this, m_szTitle); + } + + const auto NEWCLASS = fetchClass(); + if (m_szClass != NEWCLASS) { + m_szClass = NEWCLASS; + + if (m_pSelf == g_pCompositor->m_pLastWindow) { // if it's the active, let's post an event to update others + g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", m_szClass + "," + m_szTitle}); + g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)this)}); + EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock()); + } + + updateDynamicRules(); + g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock()); + updateToplevel(); + + Debug::log(LOG, "Window {:x} set class to {}", (uintptr_t)this, m_szClass); + } +} + +std::string CWindow::fetchTitle() { + if (!m_bIsX11) { + if (m_pXDGSurface && m_pXDGSurface->toplevel) + return m_pXDGSurface->toplevel->state.title; + } else { + if (m_uSurface.xwayland && m_uSurface.xwayland->title) + return m_uSurface.xwayland->title; + } + + return ""; +} + +std::string CWindow::fetchClass() { + if (!m_bIsX11) { + if (m_pXDGSurface && m_pXDGSurface->toplevel) + return m_pXDGSurface->toplevel->state.appid; + } else { + if (m_uSurface.xwayland && m_uSurface.xwayland->_class) + return m_uSurface.xwayland->_class; + } + + return ""; +} + +void CWindow::onAck(uint32_t serial) { + const auto SERIAL = std::find_if(m_vPendingSizeAcks.rbegin(), m_vPendingSizeAcks.rend(), [serial](const auto& e) { return e.first == serial; }); + + if (SERIAL == m_vPendingSizeAcks.rend()) + return; + + m_pPendingSizeAck = *SERIAL; + std::erase_if(m_vPendingSizeAcks, [&](const auto& el) { return el.first <= SERIAL->first; }); +} \ No newline at end of file diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp index 6bff095c..24d9562b 100644 --- a/src/desktop/Window.hpp +++ b/src/desktop/Window.hpp @@ -14,6 +14,8 @@ #include "DesktopTypes.hpp" #include "../helpers/signal/Signal.hpp" +class CXDGSurfaceResource; + enum eIdleInhibitMode { IDLEINHIBIT_NONE = 0, IDLEINHIBIT_ALWAYS, @@ -196,9 +198,12 @@ struct SInitialWorkspaceToken { class CWindow { public: + static PHLWINDOW create(SP); + // xwl static PHLWINDOW create(); private: + CWindow(SP resource); CWindow(); public: @@ -233,9 +238,9 @@ class CWindow { } events; union { - wlr_xdg_surface* xdg; wlr_xwayland_surface* xwayland; } m_uSurface; + WP m_pXDGSurface; // this is the position and size of the "bounding box" Vector2D m_vPosition = Vector2D(0, 0); @@ -271,6 +276,7 @@ class CWindow { bool m_bWasMaximized = false; uint64_t m_iMonitorID = -1; std::string m_szTitle = ""; + std::string m_szClass = ""; std::string m_szInitialTitle = ""; std::string m_szInitialClass = ""; PHLWORKSPACE m_pWorkspace; @@ -385,7 +391,7 @@ class CWindow { // For the list lookup bool operator==(const CWindow& rhs) { - return m_uSurface.xdg == rhs.m_uSurface.xdg && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize && + return m_pXDGSurface == rhs.m_pXDGSurface && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize && m_bFadingOut == rhs.m_bFadingOut; } @@ -424,6 +430,7 @@ class CWindow { int workspaceID(); bool onSpecialWorkspace(); void activate(bool force = false); + int surfacesCount(); int getRealBorderSize(); void updateSpecialRenderData(); @@ -450,6 +457,13 @@ class CWindow { void switchWithWindowInGroup(PHLWINDOW pWindow); void setAnimationsToMove(); void onWorkspaceAnimUpdate(); + void onUpdateState(); + void onUpdateMeta(); + std::string fetchTitle(); + std::string fetchClass(); + + // listeners + void onAck(uint32_t serial); // std::unordered_map getEnv(); @@ -457,6 +471,17 @@ class CWindow { // PHLWINDOWREF m_pSelf; + // make private once we move listeners to inside CWindow + struct { + CHyprSignalListener map; + CHyprSignalListener ack; + CHyprSignalListener unmap; + CHyprSignalListener commit; + CHyprSignalListener destroy; + CHyprSignalListener updateState; + CHyprSignalListener updateMetadata; + } listeners; + private: // For hidden windows and stuff bool m_bHidden = false; @@ -520,7 +545,7 @@ struct std::formatter : std::formatter { if (formatMonitor) std::format_to(out, ", monitor: {}", w->m_iMonitorID); if (formatClass) - std::format_to(out, ", class: {}", g_pXWaylandManager->getAppIDClass(w)); + std::format_to(out, ", class: {}", w->m_szClass); return std::format_to(out, "]"); } }; diff --git a/src/events/Events.hpp b/src/events/Events.hpp index 2e627d39..7cc0eb32 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -18,9 +18,6 @@ namespace Events { // Layer events LISTENER(newLayerSurface); - // Surface XDG (window) - LISTENER(newXDGToplevel); - // Window events DYNLISTENFUNC(commitWindow); DYNLISTENFUNC(mapWindow); diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index b85ed6e1..384ea9ed 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -8,6 +8,7 @@ #include "../render/Renderer.hpp" #include "../config/ConfigValue.hpp" #include "../protocols/LayerShell.hpp" +#include "../protocols/XDGShell.hpp" // ------------------------------------------------------------ // // __ _______ _ _ _____ ______ _______ // @@ -25,11 +26,10 @@ void addViewCoords(void* pWindow, int* x, int* y) { *y += PWINDOW->m_vRealPosition.goal().y; if (!PWINDOW->m_bIsX11 && PWINDOW->m_bIsMapped) { - wlr_box geom; - wlr_xdg_surface_get_geometry(PWINDOW->m_uSurface.xdg, &geom); + Vector2D pos = PWINDOW->m_pXDGSurface->current.geometry.pos(); - *x -= geom.x; - *y -= geom.y; + *x -= pos.x; + *y -= pos.y; } } @@ -67,11 +67,11 @@ void Events::listener_mapWindow(void* owner, void* data) { PWINDOW->m_bIsMapped = true; PWINDOW->m_bReadyToDelete = false; PWINDOW->m_bFadingOut = false; - PWINDOW->m_szTitle = g_pXWaylandManager->getTitle(PWINDOW); + PWINDOW->m_szTitle = PWINDOW->fetchTitle(); PWINDOW->m_iX11Type = PWINDOW->m_bIsX11 ? (PWINDOW->m_uSurface.xwayland->override_redirect ? 2 : 1) : 1; PWINDOW->m_bFirstMap = true; PWINDOW->m_szInitialTitle = PWINDOW->m_szTitle; - PWINDOW->m_szInitialClass = g_pXWaylandManager->getAppIDClass(PWINDOW); + PWINDOW->m_szInitialClass = PWINDOW->fetchClass(); // check for token std::string requestedWorkspace = ""; @@ -111,9 +111,6 @@ void Events::listener_mapWindow(void* owner, void* data) { if (g_pInputManager->m_bLastFocusOnLS) // waybar fix g_pInputManager->releaseAllMouseButtons(); - // Set all windows tiled regardless of anything - g_pXWaylandManager->setWindowStyleTiled(PWINDOW, WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM); - // checks if the window wants borders and sets the appropriate flag g_pXWaylandManager->checkBorders(PWINDOW); @@ -146,10 +143,8 @@ void Events::listener_mapWindow(void* owner, void* data) { } // window rules - PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false); - bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen || - (!PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xdg->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL && PWINDOW->m_uSurface.xdg->toplevel->requested.fullscreen) || - (PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->fullscreen); + PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false); + bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->fullscreen); bool requestsFakeFullscreen = false; bool requestsMaximize = false; bool overridingNoFullscreen = false; @@ -508,19 +503,7 @@ void Events::listener_mapWindow(void* owner, void* data) { PWINDOW->m_fDimPercent.setValueAndWarp(0); } - if (!PWINDOW->m_bIsX11) { - PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.set_title, &Events::listener_setTitleWindow, PWINDOW.get(), "XDG Window Late"); - PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_maximize, &Events::listener_requestMaximize, PWINDOW.get(), - "XDG Window Late"); - PWINDOW->hyprListener_requestMinimize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_minimize, &Events::listener_requestMinimize, PWINDOW.get(), - "XDG Window Late"); - PWINDOW->hyprListener_requestMove.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_move, &Events::listener_requestMove, PWINDOW.get(), "XDG Window Late"); - PWINDOW->hyprListener_requestResize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_resize, &Events::listener_requestResize, PWINDOW.get(), - "XDG Window Late"); - PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW.get(), - "XDG Window Late"); - PWINDOW->hyprListener_ackConfigure.initCallback(&PWINDOW->m_uSurface.xdg->events.ack_configure, &Events::listener_ackConfigure, PWINDOW.get(), "XDG Window Late"); - } else { + if (PWINDOW->m_bIsX11) { PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW.get(), "XWayland Window Late"); PWINDOW->hyprListener_activateX11.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_activate, &Events::listener_activateX11, PWINDOW.get(), @@ -573,7 +556,7 @@ void Events::listener_mapWindow(void* owner, void* data) { if (*PSWALLOW && std::string{*PSWALLOWREGEX} != STRVAL_EMPTY) { // don't swallow ourselves std::regex rgx(*PSWALLOWREGEX); - if (!std::regex_match(g_pXWaylandManager->getAppIDClass(PWINDOW), rgx)) { + if (!std::regex_match(PWINDOW->m_szClass, rgx)) { // check parent int ppid = getPPIDof(PWINDOW->getPID()); @@ -615,12 +598,12 @@ void Events::listener_mapWindow(void* owner, void* data) { } if (finalFound) { - bool valid = std::regex_match(g_pXWaylandManager->getAppIDClass(finalFound), rgx); + bool valid = std::regex_match(PWINDOW->m_szClass, rgx); if (std::string{*PSWALLOWEXREGEX} != STRVAL_EMPTY) { std::regex exc(*PSWALLOWEXREGEX); - valid = valid && !std::regex_match(g_pXWaylandManager->getTitle(finalFound), exc); + valid = valid && !std::regex_match(PWINDOW->m_szTitle, exc); } // check if it's the window we want & not exempt from getting swallowed @@ -644,7 +627,7 @@ void Events::listener_mapWindow(void* owner, void* data) { Debug::log(LOG, "Map request dispatched, monitor {}, window pos: {:5j}, window size: {:5j}", PMONITOR->szName, PWINDOW->m_vRealPosition.goal(), PWINDOW->m_vRealSize.goal()); auto workspaceID = requestedWorkspace != "" ? requestedWorkspace : PWORKSPACE->m_szName; - g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, g_pXWaylandManager->getAppIDClass(PWINDOW), PWINDOW->m_szTitle)}); + g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, PWINDOW->m_szClass, PWINDOW->m_szTitle)}); EMIT_HOOK_EVENT("openWindow", PWINDOW); // apply data from default decos. Borders, shadows. @@ -705,15 +688,6 @@ void Events::listener_unmapWindow(void* owner, void* data) { g_pProtocolManager->m_pToplevelExportProtocolManager->onWindowUnmap(PWINDOW); if (!PWINDOW->m_bIsX11) { - Debug::log(LOG, "Unregistered late callbacks XDG"); - PWINDOW->hyprListener_setTitleWindow.removeCallback(); - PWINDOW->hyprListener_requestMaximize.removeCallback(); - PWINDOW->hyprListener_requestMinimize.removeCallback(); - PWINDOW->hyprListener_requestMove.removeCallback(); - PWINDOW->hyprListener_requestResize.removeCallback(); - PWINDOW->hyprListener_fullscreenWindow.removeCallback(); - PWINDOW->hyprListener_ackConfigure.removeCallback(); - } else { Debug::log(LOG, "Unregistered late callbacks XWL"); PWINDOW->hyprListener_fullscreenWindow.removeCallback(); PWINDOW->hyprListener_activateX11.removeCallback(); @@ -804,29 +778,15 @@ void Events::listener_unmapWindow(void* owner, void* data) { PWINDOW->onUnmap(); } -void Events::listener_ackConfigure(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - const auto E = (wlr_xdg_surface_configure*)data; - - // find last matching serial - const auto SERIAL = std::find_if(PWINDOW->m_vPendingSizeAcks.rbegin(), PWINDOW->m_vPendingSizeAcks.rend(), [&](const auto& e) { return e.first == E->serial; }); - - if (SERIAL == PWINDOW->m_vPendingSizeAcks.rend()) - return; - - PWINDOW->m_pPendingSizeAck = *SERIAL; - std::erase_if(PWINDOW->m_vPendingSizeAcks, [&](const auto& el) { return el.first == SERIAL->first; }); -} - void Events::listener_commitWindow(void* owner, void* data) { PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - if (!PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xdg->initial_commit) { + if (!PWINDOW->m_bIsX11 && PWINDOW->m_pXDGSurface->initialCommit) { Vector2D predSize = g_pLayoutManager->getCurrentLayout()->predictSizeForNewWindow(PWINDOW); Debug::log(LOG, "Layout predicts size {} for {}", predSize, PWINDOW); - wlr_xdg_toplevel_set_size(PWINDOW->m_uSurface.xdg->toplevel, predSize.x, predSize.y); + PWINDOW->m_pXDGSurface->toplevel->setSize(predSize); return; } @@ -841,8 +801,8 @@ void Events::listener_commitWindow(void* owner, void* data) { } if (!PWINDOW->m_bIsX11 && !PWINDOW->m_bIsFullscreen && PWINDOW->m_bIsFloating) { - const auto MINSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.min_width, PWINDOW->m_uSurface.xdg->toplevel->current.min_height}; - const auto MAXSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.max_width, PWINDOW->m_uSurface.xdg->toplevel->current.max_height}; + const auto MINSIZE = PWINDOW->m_pXDGSurface->toplevel->current.minSize; + const auto MAXSIZE = PWINDOW->m_pXDGSurface->toplevel->current.maxSize; if (MAXSIZE > Vector2D{1, 1}) { const auto REALSIZE = PWINDOW->m_vRealSize.goal(); @@ -861,7 +821,7 @@ void Events::listener_commitWindow(void* owner, void* data) { PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.goal() + DELTA / 2.0; PWINDOW->m_vRealSize = newSize; - g_pXWaylandManager->setWindowSize(PWINDOW, newSize, true); + g_pXWaylandManager->setWindowSize(PWINDOW, newSize); g_pHyprRenderer->damageWindow(PWINDOW); } } @@ -918,11 +878,13 @@ void Events::listener_destroyWindow(void* owner, void* data) { PWINDOW->hyprListener_associateX11.removeCallback(); PWINDOW->hyprListener_dissociateX11.removeCallback(); + PWINDOW->listeners = {}; + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW); PWINDOW->m_bReadyToDelete = true; - PWINDOW->m_uSurface.xdg = nullptr; + PWINDOW->m_pXDGSurface.reset(); if (!PWINDOW->m_bFadingOut) { Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW); @@ -936,31 +898,14 @@ void Events::listener_setTitleWindow(void* owner, void* data) { if (!validMapped(PWINDOW)) return; - const auto NEWTITLE = g_pXWaylandManager->getTitle(PWINDOW); - - if (NEWTITLE == PWINDOW->m_szTitle) - return; - - PWINDOW->m_szTitle = NEWTITLE; - g_pEventManager->postEvent(SHyprIPCEvent{"windowtitle", std::format("{:x}", (uintptr_t)PWINDOW.get())}); - EMIT_HOOK_EVENT("windowTitle", PWINDOW); - - if (PWINDOW == g_pCompositor->m_pLastWindow.lock()) { // if it's the active, let's post an event to update others - g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(PWINDOW) + "," + PWINDOW->m_szTitle}); - g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)PWINDOW.get())}); - EMIT_HOOK_EVENT("activeWindow", PWINDOW); - } - - PWINDOW->updateDynamicRules(); - g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); - PWINDOW->updateToplevel(); - - Debug::log(LOG, "Window {:x} set title to {}", PWINDOW, PWINDOW->m_szTitle); + PWINDOW->onUpdateMeta(); } void Events::listener_fullscreenWindow(void* owner, void* data) { PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); + // x11 only + if (!PWINDOW->m_bIsMapped) { PWINDOW->m_bWantsInitialFullscreen = true; return; @@ -971,41 +916,13 @@ void Events::listener_fullscreenWindow(void* owner, void* data) { bool requestedFullState = false; - if (!PWINDOW->m_bIsX11) { - const auto REQUESTED = &PWINDOW->m_uSurface.xdg->toplevel->requested; + if (!PWINDOW->m_uSurface.xwayland->surface->mapped) + return; - if (REQUESTED->fullscreen && PWINDOW->m_bIsFullscreen) { - const auto PWORKSPACE = PWINDOW->m_pWorkspace; - if (PWORKSPACE->m_efFullscreenMode != FULLSCREEN_FULL) { - // Store that we were maximized - PWINDOW->m_bWasMaximized = true; - g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_MAXIMIZED); - g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_FULL); - } else - PWINDOW->m_bWasMaximized = false; - } else if (REQUESTED->fullscreen != PWINDOW->m_bIsFullscreen && !PWINDOW->m_bFakeFullscreenState) { - g_pCompositor->setWindowFullscreen(PWINDOW, REQUESTED->fullscreen, FULLSCREEN_FULL); - if (PWINDOW->m_bWasMaximized && !REQUESTED->fullscreen) { - // Was maximized before the fullscreen request, return now back to maximized instead of normal - g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_MAXIMIZED); - } - } + if (!PWINDOW->m_bFakeFullscreenState) + g_pCompositor->setWindowFullscreen(PWINDOW, PWINDOW->m_uSurface.xwayland->fullscreen, FULLSCREEN_FULL); - // Disable the maximize flag when we receive a de-fullscreen request - PWINDOW->m_bWasMaximized &= REQUESTED->fullscreen; - - requestedFullState = REQUESTED->fullscreen; - - wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg); - } else { - if (!PWINDOW->m_uSurface.xwayland->surface->mapped) - return; - - if (!PWINDOW->m_bFakeFullscreenState) - g_pCompositor->setWindowFullscreen(PWINDOW, PWINDOW->m_uSurface.xwayland->fullscreen, FULLSCREEN_FULL); - - requestedFullState = PWINDOW->m_uSurface.xwayland->fullscreen; - } + requestedFullState = PWINDOW->m_uSurface.xwayland->fullscreen; if (!requestedFullState && PWINDOW->m_bFakeFullscreenState) { g_pXWaylandManager->setWindowFullscreen(PWINDOW, false); // fixes for apps expecting a de-fullscreen (e.g. ff) @@ -1177,7 +1094,7 @@ void Events::listener_associateX11(void* owner, void* data) { PWINDOW->hyprListener_mapWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.map, &Events::listener_mapWindow, PWINDOW.get(), "XWayland Window"); PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.commit, &Events::listener_commitWindow, PWINDOW.get(), "XWayland Window"); - PWINDOW->m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(PWINDOW), PWINDOW); + PWINDOW->m_pWLSurface.assign(PWINDOW->m_uSurface.xwayland->surface, PWINDOW); } void Events::listener_dissociateX11(void* owner, void* data) { @@ -1211,23 +1128,6 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) { PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW.get(), "XWayland Window"); } -void Events::listener_newXDGToplevel(wl_listener* listener, void* data) { - // A window got opened - const auto XDGTOPLEVEL = (wlr_xdg_toplevel*)data; - const auto XDGSURFACE = XDGTOPLEVEL->base; - - Debug::log(LOG, "New XDG Toplevel created. (class: {})", XDGSURFACE->toplevel->app_id ? XDGSURFACE->toplevel->app_id : "null"); - - const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(CWindow::create()); - PNEWWINDOW->m_uSurface.xdg = XDGSURFACE; - - PNEWWINDOW->hyprListener_mapWindow.initCallback(&XDGSURFACE->surface->events.map, &Events::listener_mapWindow, PNEWWINDOW.get(), "XDG Window"); - PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XDGSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW.get(), "XDG Window"); - PNEWWINDOW->hyprListener_commitWindow.initCallback(&XDGSURFACE->surface->events.commit, &Events::listener_commitWindow, PNEWWINDOW.get(), "XDG Window"); - - PNEWWINDOW->m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(PNEWWINDOW), PNEWWINDOW); -} - void Events::listener_requestMaximize(void* owner, void* data) { PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); @@ -1240,7 +1140,6 @@ void Events::listener_requestMaximize(void* owner, void* data) { g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen, FULLSCREEN_MAXIMIZED); // this will be rejected if there already is a fullscreen window - wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg); } else { if (!PWINDOW->m_bIsMapped || PWINDOW->m_iX11Type != 1) return; @@ -1269,17 +1168,3 @@ void Events::listener_requestMinimize(void* owner, void* data) { EMIT_HOOK_EVENT("minimize", (std::vector{PWINDOW, (int64_t)(1)})); } } - -void Events::listener_requestMove(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - - // ignore - wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg); -} - -void Events::listener_requestResize(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - - // ignore - wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg); -} diff --git a/src/includes.hpp b/src/includes.hpp index c75796b9..dfbe0221 100644 --- a/src/includes.hpp +++ b/src/includes.hpp @@ -52,8 +52,6 @@ extern "C" { #include #include #include -#include -#include #include #include #include diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp index 6f7252d2..a31180e1 100644 --- a/src/layout/IHyprLayout.cpp +++ b/src/layout/IHyprLayout.cpp @@ -4,6 +4,7 @@ #include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "../config/ConfigValue.hpp" #include "../desktop/Window.hpp" +#include "../protocols/XDGShell.hpp" void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) { if (pWindow->m_bIsFloating) { @@ -140,8 +141,12 @@ void IHyprLayout::onWindowCreatedFloating(PHLWINDOW pWindow) { // TODO: detect a popup in a more consistent way. if ((desiredGeometry.x == 0 && desiredGeometry.y == 0) || !visible || !pWindow->m_bIsX11) { - // if it's not, fall back to the center placement - pWindow->m_vRealPosition = PMONITOR->vecPosition + Vector2D((PMONITOR->vecSize.x - desiredGeometry.width) / 2.f, (PMONITOR->vecSize.y - desiredGeometry.height) / 2.f); + // if the pos isn't set, fall back to the center placement if it's not a child, otherwise middle of parent if available + if (!pWindow->m_bIsX11 && pWindow->m_pXDGSurface->toplevel->parent && validMapped(pWindow->m_pXDGSurface->toplevel->parent->window)) + pWindow->m_vRealPosition = pWindow->m_pXDGSurface->toplevel->parent->window->m_vRealPosition.goal() + + pWindow->m_pXDGSurface->toplevel->parent->window->m_vRealSize.goal() / 2.F - desiredGeometry.size() / 2.F; + else + pWindow->m_vRealPosition = PMONITOR->vecPosition + desiredGeometry.size() / 2.F; } else { // if it is, we respect where it wants to put itself, but apply monitor offset if outside // most of these are popups @@ -693,7 +698,7 @@ Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) { else sizePredicted = predictSizeForNewWindowFloating(pWindow); - Vector2D maxSize = Vector2D{pWindow->m_uSurface.xdg->toplevel->pending.max_width, pWindow->m_uSurface.xdg->toplevel->pending.max_height}; + Vector2D maxSize = pWindow->m_pXDGSurface->toplevel->pending.maxSize; if ((maxSize.x > 0 && maxSize.x < sizePredicted.x) || (maxSize.y > 0 && maxSize.y < sizePredicted.y)) sizePredicted = {}; diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index f853e16f..e53eb111 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -29,6 +29,7 @@ #include "../protocols/LayerShell.hpp" #include "../protocols/PresentationTime.hpp" #include "../protocols/core/Seat.hpp" +#include "../protocols/XDGShell.hpp" CProtocolManager::CProtocolManager() { @@ -64,6 +65,7 @@ CProtocolManager::CProtocolManager() { PROTO::tablet = std::make_unique(&zwp_tablet_manager_v2_interface, 1, "TabletV2"); PROTO::layerShell = std::make_unique(&zwlr_layer_shell_v1_interface, 5, "LayerShell"); PROTO::presentation = std::make_unique(&wp_presentation_interface, 1, "Presentation"); + PROTO::xdgShell = std::make_unique(&xdg_wm_base_interface, 6, "XDGShell"); // Old protocol implementations. // TODO: rewrite them to use hyprwayland-scanner. diff --git a/src/managers/XWaylandManager.cpp b/src/managers/XWaylandManager.cpp index 423c45ca..390c6ece 100644 --- a/src/managers/XWaylandManager.cpp +++ b/src/managers/XWaylandManager.cpp @@ -2,6 +2,7 @@ #include "../Compositor.hpp" #include "../events/Events.hpp" #include "../config/ConfigValue.hpp" +#include "../protocols/XDGShell.hpp" #define OUTPUT_MANAGER_VERSION 3 #define OUTPUT_DONE_DEPRECATED_SINCE_VERSION 3 @@ -34,27 +35,32 @@ CHyprXWaylandManager::~CHyprXWaylandManager() { } wlr_surface* CHyprXWaylandManager::getWindowSurface(PHLWINDOW pWindow) { - if (pWindow->m_bIsX11) - return pWindow->m_uSurface.xwayland->surface; - - return pWindow->m_uSurface.xdg->surface; + return pWindow->m_pWLSurface.wlr(); } void CHyprXWaylandManager::activateSurface(wlr_surface* pSurface, bool activate) { if (!pSurface) return; - if (wlr_xdg_surface_try_from_wlr_surface(pSurface)) { - if (const auto PSURF = wlr_xdg_surface_try_from_wlr_surface(pSurface); PSURF && PSURF->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) - wlr_xdg_toplevel_set_activated(PSURF->toplevel, activate); - - } else if (wlr_xwayland_surface_try_from_wlr_surface(pSurface)) { + if (wlr_xwayland_surface_try_from_wlr_surface(pSurface)) { const auto XSURF = wlr_xwayland_surface_try_from_wlr_surface(pSurface); wlr_xwayland_surface_activate(XSURF, activate); if (activate && !XSURF->override_redirect) wlr_xwayland_surface_restack(XSURF, nullptr, XCB_STACK_MODE_ABOVE); } + + // TODO: + // this cannot be nicely done until we rewrite wlr_surface + for (auto& w : g_pCompositor->m_vWindows) { + if (w->m_bIsX11 || !w->m_bIsMapped) + continue; + + if (w->m_pWLSurface.wlr() != pSurface) + continue; + + w->m_pXDGSurface->toplevel->setActive(activate); + } } void CHyprXWaylandManager::activateWindow(PHLWINDOW pWindow, bool activate) { @@ -68,8 +74,8 @@ void CHyprXWaylandManager::activateWindow(PHLWINDOW pWindow, bool activate) { } wlr_xwayland_surface_activate(pWindow->m_uSurface.xwayland, activate); - } else - wlr_xdg_toplevel_set_activated(pWindow->m_uSurface.xdg->toplevel, activate); + } else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) + pWindow->m_pXDGSurface->toplevel->setActive(activate); if (activate) { g_pCompositor->m_pLastFocus = getWindowSurface(pWindow); @@ -95,64 +101,15 @@ void CHyprXWaylandManager::getGeometryForWindow(PHLWINDOW pWindow, CBox* pbox) { pbox->width = pWindow->m_uSurface.xwayland->width; pbox->height = pWindow->m_uSurface.xwayland->height; } - } else { - wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, pbox->pWlr()); - pbox->applyFromWlr(); - } -} - -std::string CHyprXWaylandManager::getTitle(PHLWINDOW pWindow) { - try { - if (pWindow->m_bIsX11) { - if (!pWindow->m_bIsMapped) - return ""; - - if (pWindow->m_uSurface.xwayland && pWindow->m_uSurface.xwayland->title) { - return std::string(pWindow->m_uSurface.xwayland->title); - } - } else if (pWindow->m_uSurface.xdg) { - if (pWindow->m_bFadingOut || !pWindow->m_uSurface.xdg) - return ""; - - if (pWindow->m_uSurface.xdg->toplevel && pWindow->m_uSurface.xdg->toplevel->title) { - return std::string(pWindow->m_uSurface.xdg->toplevel->title); - } - } else { - return ""; - } - } catch (...) { Debug::log(ERR, "Error in getTitle (probably null title)"); } - - return ""; -} - -std::string CHyprXWaylandManager::getAppIDClass(PHLWINDOW pWindow) { - try { - if (pWindow->m_bIsX11) { - if (!pWindow->m_bIsMapped) - return ""; - - if (pWindow->m_uSurface.xwayland && pWindow->m_uSurface.xwayland->_class) { - return std::string(pWindow->m_uSurface.xwayland->_class); - } - } else if (pWindow->m_uSurface.xdg) { - if (pWindow->m_bFadingOut || !pWindow->m_uSurface.xdg) - return ""; - - if (pWindow->m_uSurface.xdg->toplevel && pWindow->m_uSurface.xdg->toplevel->app_id) { - return std::string(pWindow->m_uSurface.xdg->toplevel->app_id); - } - } else - return ""; - } catch (std::logic_error& e) { Debug::log(ERR, "Error in getAppIDClass: {}", e.what()); } - - return ""; + } else if (pWindow->m_pXDGSurface) + *pbox = pWindow->m_pXDGSurface->current.geometry; } void CHyprXWaylandManager::sendCloseWindow(PHLWINDOW pWindow) { if (pWindow->m_bIsX11) wlr_xwayland_surface_close(pWindow->m_uSurface.xwayland); - else - wlr_xdg_toplevel_send_close(pWindow->m_uSurface.xdg->toplevel); + else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) + pWindow->m_pXDGSurface->toplevel->close(); } void CHyprXWaylandManager::setWindowSize(PHLWINDOW pWindow, Vector2D size, bool force) { @@ -189,23 +146,12 @@ void CHyprXWaylandManager::setWindowSize(PHLWINDOW pWindow, Vector2D size, bool if (pWindow->m_bIsX11) wlr_xwayland_surface_configure(pWindow->m_uSurface.xwayland, windowPos.x, windowPos.y, size.x, size.y); - else - pWindow->m_vPendingSizeAcks.push_back(std::make_pair<>(wlr_xdg_toplevel_set_size(pWindow->m_uSurface.xdg->toplevel, size.x, size.y), size.floor())); -} - -void CHyprXWaylandManager::setWindowStyleTiled(PHLWINDOW pWindow, uint32_t edgez) { - if (pWindow->m_bIsX11) - return; - - wlr_xdg_toplevel_set_tiled(pWindow->m_uSurface.xdg->toplevel, edgez); - wlr_xdg_toplevel_set_maximized(pWindow->m_uSurface.xdg->toplevel, true); + else if (pWindow->m_pXDGSurface->toplevel) + pWindow->m_vPendingSizeAcks.push_back(std::make_pair<>(pWindow->m_pXDGSurface->toplevel->setSize(size), size.floor())); } wlr_surface* CHyprXWaylandManager::surfaceAt(PHLWINDOW pWindow, const Vector2D& client, Vector2D& surface) { - if (pWindow->m_bIsX11) - return wlr_surface_surface_at(pWindow->m_uSurface.xwayland->surface, client.x, client.y, &surface.x, &surface.y); - - return wlr_xdg_surface_surface_at(pWindow->m_uSurface.xdg, client.x, client.y, &surface.x, &surface.y); + return wlr_surface_surface_at(pWindow->m_pWLSurface.wlr(), client.x, client.y, &surface.x, &surface.y); } bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) { @@ -251,10 +197,10 @@ bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) { if (SIZEHINTS && (pWindow->m_uSurface.xwayland->parent || ((SIZEHINTS->min_width == SIZEHINTS->max_width) && (SIZEHINTS->min_height == SIZEHINTS->max_height)))) return true; } else { - const auto PSTATE = pending ? &pWindow->m_uSurface.xdg->toplevel->pending : &pWindow->m_uSurface.xdg->toplevel->current; + const auto PSTATE = pending ? &pWindow->m_pXDGSurface->toplevel->pending : &pWindow->m_pXDGSurface->toplevel->current; - if ((PSTATE->min_width != 0 && PSTATE->min_height != 0 && (PSTATE->min_width == PSTATE->max_width || PSTATE->min_height == PSTATE->max_height)) || - pWindow->m_uSurface.xdg->toplevel->parent) + if (pWindow->m_pXDGSurface->toplevel->parent || + (PSTATE->minSize.x != 0 && PSTATE->minSize.y != 0 && (PSTATE->minSize.x == PSTATE->maxSize.x || PSTATE->minSize.y == PSTATE->maxSize.y))) return true; } @@ -297,20 +243,19 @@ void CHyprXWaylandManager::checkBorders(PHLWINDOW pWindow) { void CHyprXWaylandManager::setWindowFullscreen(PHLWINDOW pWindow, bool fullscreen) { if (pWindow->m_bIsX11) wlr_xwayland_surface_set_fullscreen(pWindow->m_uSurface.xwayland, fullscreen); - else - wlr_xdg_toplevel_set_fullscreen(pWindow->m_uSurface.xdg->toplevel, fullscreen); + else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) + pWindow->m_pXDGSurface->toplevel->setFullscreen(fullscreen); } Vector2D CHyprXWaylandManager::getMaxSizeForWindow(PHLWINDOW pWindow) { if (!validMapped(pWindow)) return Vector2D(99999, 99999); - if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_uSurface.xdg->toplevel) || - pWindow->m_sAdditionalConfigData.noMaxSize) + if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_pXDGSurface->toplevel) || pWindow->m_sAdditionalConfigData.noMaxSize) return Vector2D(99999, 99999); auto MAXSIZE = pWindow->m_bIsX11 ? Vector2D(pWindow->m_uSurface.xwayland->size_hints->max_width, pWindow->m_uSurface.xwayland->size_hints->max_height) : - Vector2D(pWindow->m_uSurface.xdg->toplevel->current.max_width, pWindow->m_uSurface.xdg->toplevel->current.max_height); + pWindow->m_pXDGSurface->toplevel->current.maxSize; if (MAXSIZE.x < 5) MAXSIZE.x = 99999; @@ -324,11 +269,11 @@ Vector2D CHyprXWaylandManager::getMinSizeForWindow(PHLWINDOW pWindow) { if (!validMapped(pWindow)) return Vector2D(0, 0); - if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_uSurface.xdg->toplevel)) + if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_pXDGSurface->toplevel)) return Vector2D(0, 0); auto MINSIZE = pWindow->m_bIsX11 ? Vector2D(pWindow->m_uSurface.xwayland->size_hints->min_width, pWindow->m_uSurface.xwayland->size_hints->min_height) : - Vector2D(pWindow->m_uSurface.xdg->toplevel->current.min_width, pWindow->m_uSurface.xdg->toplevel->current.min_height); + pWindow->m_pXDGSurface->toplevel->current.minSize; MINSIZE = MINSIZE.clamp({1, 1}); diff --git a/src/managers/XWaylandManager.hpp b/src/managers/XWaylandManager.hpp index 6edadd71..8889eb61 100644 --- a/src/managers/XWaylandManager.hpp +++ b/src/managers/XWaylandManager.hpp @@ -17,11 +17,8 @@ class CHyprXWaylandManager { void activateSurface(wlr_surface*, bool); void activateWindow(PHLWINDOW, bool); void getGeometryForWindow(PHLWINDOW, CBox*); - std::string getTitle(PHLWINDOW); - std::string getAppIDClass(PHLWINDOW); void sendCloseWindow(PHLWINDOW); void setWindowSize(PHLWINDOW, Vector2D, bool force = false); - void setWindowStyleTiled(PHLWINDOW, uint32_t); void setWindowFullscreen(PHLWINDOW, bool); wlr_surface* surfaceAt(PHLWINDOW, const Vector2D&, Vector2D&); bool shouldBeFloated(PHLWINDOW, bool pending = false); diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 62d9e687..bb0ab65a 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -15,6 +15,7 @@ #include "../../protocols/VirtualPointer.hpp" #include "../../protocols/LayerShell.hpp" #include "../../protocols/core/Seat.hpp" +#include "../../protocols/XDGShell.hpp" #include "../../devices/Mouse.hpp" #include "../../devices/VirtualPointer.hpp" @@ -387,10 +388,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (pFoundWindow && !pFoundWindow->m_bIsX11 && surfacePos != Vector2D(-1337, -1337)) { // calc for oversized windows... fucking bullshit. - wlr_box geom; - wlr_xdg_surface_get_geometry(pFoundWindow->m_uSurface.xdg, &geom); + CBox geom = pFoundWindow->m_pXDGSurface->current.geometry; - surfaceLocal = mouseCoords - surfacePos + Vector2D(geom.x, geom.y); + surfaceLocal = mouseCoords - surfacePos + geom.pos(); } if (pFoundWindow && pFoundWindow->m_bIsX11) // for x11 force scale zero diff --git a/src/protocols/ForeignToplevel.cpp b/src/protocols/ForeignToplevel.cpp index 0d2a7e57..40420da7 100644 --- a/src/protocols/ForeignToplevel.cpp +++ b/src/protocols/ForeignToplevel.cpp @@ -91,7 +91,7 @@ void CForeignToplevelList::onClass(PHLWINDOW pWindow) { if (!H || H->closed) return; - H->resource->sendAppId(g_pXWaylandManager->getAppIDClass(pWindow).c_str()); + H->resource->sendAppId(pWindow->m_szClass.c_str()); } void CForeignToplevelList::onUnmap(PHLWINDOW pWindow) { diff --git a/src/protocols/ForeignToplevelWlr.cpp b/src/protocols/ForeignToplevelWlr.cpp index 1762ad2a..782fbc0e 100644 --- a/src/protocols/ForeignToplevelWlr.cpp +++ b/src/protocols/ForeignToplevelWlr.cpp @@ -195,7 +195,7 @@ void CForeignToplevelWlrManager::onMap(PHLWINDOW pWindow) { LOGM(LOG, "Newly mapped window {:016x}", (uintptr_t)pWindow.get()); resource->sendToplevel(NEWHANDLE->resource.get()); - NEWHANDLE->resource->sendAppId(g_pXWaylandManager->getAppIDClass(pWindow).c_str()); + NEWHANDLE->resource->sendAppId(pWindow->m_szClass.c_str()); NEWHANDLE->resource->sendTitle(pWindow->m_szTitle.c_str()); if (const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); PMONITOR) NEWHANDLE->sendMonitor(PMONITOR); @@ -231,7 +231,7 @@ void CForeignToplevelWlrManager::onClass(PHLWINDOW pWindow) { if (!H || H->closed) return; - H->resource->sendAppId(g_pXWaylandManager->getAppIDClass(pWindow).c_str()); + H->resource->sendAppId(pWindow->m_szClass.c_str()); H->resource->sendDone(); } diff --git a/src/protocols/LayerShell.cpp b/src/protocols/LayerShell.cpp index ced765fd..962b89a3 100644 --- a/src/protocols/LayerShell.cpp +++ b/src/protocols/LayerShell.cpp @@ -1,5 +1,6 @@ #include "LayerShell.hpp" #include "../Compositor.hpp" +#include "XDGShell.hpp" #define LOGM PROTO::layerShell->protoLog @@ -118,14 +119,14 @@ CLayerShellResource::CLayerShellResource(SP resource_, wlr_ }); resource->setGetPopup([this](CZwlrLayerSurfaceV1* r, wl_resource* popup_) { - auto popup = wlr_xdg_popup_from_resource(popup_); + auto popup = CXDGPopupResource::fromResource(popup_); - if (popup->parent) { + if (popup->taken) { r->error(-1, "Parent already exists!"); return; } - popup->parent = surface; + popup->taken = true; events.newPopup.emit(popup); }); diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp new file mode 100644 index 00000000..fcb2d63b --- /dev/null +++ b/src/protocols/XDGShell.cpp @@ -0,0 +1,697 @@ +#include "XDGShell.hpp" +#include +#include "../Compositor.hpp" + +#define LOGM PROTO::xdgShell->protoLog + +CXDGPopupResource::CXDGPopupResource(SP resource_, SP owner_, SP surface_, SP positioner) : + surface(surface_), parent(owner_), resource(resource_), positionerRules(positioner) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CXdgPopup* r) { + if (surface && surface->mapped) + surface->events.unmap.emit(); + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + resource->setOnDestroy([this](CXdgPopup* r) { + if (surface && surface->mapped) + surface->events.unmap.emit(); + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + + resource->setReposition([this](CXdgPopup* r, wl_resource* positionerRes, uint32_t token) { + LOGM(LOG, "Popup {:x} asks for reposition", (uintptr_t)this); + lastRepositionToken = token; + auto pos = CXDGPositionerResource::fromResource(positionerRes); + if (!pos) + return; + positionerRules = CXDGPositionerRules{pos}; + events.reposition.emit(); + }); + + if (parent) + taken = true; +} + +CXDGPopupResource::~CXDGPopupResource() { + events.destroy.emit(); +} + +void CXDGPopupResource::applyPositioning(const CBox& box, const Vector2D& t1coord) { + CBox constraint = box.copy().translate(surface->pending.geometry.pos()); + + geometry = positionerRules.getPosition(constraint, accumulateParentOffset() + t1coord); + + LOGM(LOG, "Popup {:x} gets unconstrained to {} {}", (uintptr_t)this, geometry.pos(), geometry.size()); + + configure(geometry); + + if (lastRepositionToken) + repositioned(); +} + +Vector2D CXDGPopupResource::accumulateParentOffset() { + SP current = parent.lock(); + Vector2D off; + while (current) { + off += current->current.geometry.pos(); + if (current->popup) { + off += current->popup->geometry.pos(); + current = current->popup->parent.lock(); + } else + break; + } + return off; +} + +SP CXDGPopupResource::fromResource(wl_resource* res) { + auto data = (CXDGPopupResource*)(((CXdgPopup*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CXDGPopupResource::good() { + return resource->resource(); +} + +void CXDGPopupResource::configure(const CBox& box) { + resource->sendConfigure(box.x, box.y, box.w, box.h); + if (surface) + surface->scheduleConfigure(); +} + +void CXDGPopupResource::done() { + resource->sendPopupDone(); +} + +void CXDGPopupResource::repositioned() { + if (!lastRepositionToken) + return; + + LOGM(LOG, "repositioned: sending reposition token {}", lastRepositionToken); + + resource->sendRepositioned(lastRepositionToken); + lastRepositionToken = 0; +} + +CXDGToplevelResource::CXDGToplevelResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CXdgToplevel* r) { + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + resource->setOnDestroy([this](CXdgToplevel* r) { + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + + if (resource->version() >= 5) { + wl_array arr; + wl_array_init(&arr); + auto p = (uint32_t*)wl_array_add(&arr, sizeof(uint32_t)); + *p = XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN; + p = (uint32_t*)wl_array_add(&arr, sizeof(uint32_t)); + *p = XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE; + resource->sendWmCapabilities(&arr); + wl_array_release(&arr); + } + + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_LEFT); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_RIGHT); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_TOP); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_BOTTOM); + + resource->setSetTitle([this](CXdgToplevel* r, const char* t) { + state.title = t; + events.metadataChanged.emit(); + }); + + resource->setSetAppId([this](CXdgToplevel* r, const char* id) { + state.appid = id; + events.metadataChanged.emit(); + }); + + resource->setSetMaxSize([this](CXdgToplevel* r, int32_t x, int32_t y) { + pending.maxSize = {x, y}; + events.sizeLimitsChanged.emit(); + }); + + resource->setSetMinSize([this](CXdgToplevel* r, int32_t x, int32_t y) { + pending.minSize = {x, y}; + events.sizeLimitsChanged.emit(); + }); + + resource->setSetMaximized([this](CXdgToplevel* r) { + state.requestsMaximize = true; + events.stateChanged.emit(); + state.requestsMaximize.reset(); + }); + + resource->setUnsetMaximized([this](CXdgToplevel* r) { + state.requestsMaximize = false; + events.stateChanged.emit(); + state.requestsMaximize.reset(); + }); + + resource->setSetFullscreen([this](CXdgToplevel* r, wl_resource* output) { + state.requestsFullscreen = true; + events.stateChanged.emit(); + state.requestsFullscreen.reset(); + }); + + resource->setUnsetFullscreen([this](CXdgToplevel* r) { + state.requestsFullscreen = false; + events.stateChanged.emit(); + state.requestsFullscreen.reset(); + }); + + resource->setSetMinimized([this](CXdgToplevel* r) { + state.requestsMinimize = true; + events.stateChanged.emit(); + state.requestsFullscreen.reset(); + }); + + resource->setSetParent([this](CXdgToplevel* r, wl_resource* parentR) { + auto newp = parentR ? CXDGToplevelResource::fromResource(parentR) : nullptr; + parent = newp; + + LOGM(LOG, "Toplevel {:x} sets parent to {:x}", (uintptr_t)this, (uintptr_t)newp.get()); + }); +} + +CXDGToplevelResource::~CXDGToplevelResource() { + events.destroy.emit(); +} + +SP CXDGToplevelResource::fromResource(wl_resource* res) { + auto data = (CXDGToplevelResource*)(((CXdgToplevel*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CXDGToplevelResource::good() { + return resource->resource(); +} + +uint32_t CXDGToplevelResource::setSize(const Vector2D& size) { + pendingApply.size = size; + applyState(); + return owner->scheduleConfigure(); +} + +uint32_t CXDGToplevelResource::setMaximized(bool maximized) { + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_MAXIMIZED) != pendingApply.states.end(); + + if (maximized == set) + return owner->scheduledSerial; + + if (maximized && !set) + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_MAXIMIZED); + else if (!maximized && set) + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_MAXIMIZED); + applyState(); + return owner->scheduleConfigure(); +} + +uint32_t CXDGToplevelResource::setFullscreen(bool fullscreen) { + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_FULLSCREEN) != pendingApply.states.end(); + + if (fullscreen == set) + return owner->scheduledSerial; + + if (fullscreen && !set) + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_FULLSCREEN); + else if (!fullscreen && set) + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_FULLSCREEN); + applyState(); + return owner->scheduleConfigure(); +} + +uint32_t CXDGToplevelResource::setActive(bool active) { + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_ACTIVATED) != pendingApply.states.end(); + + if (active == set) + return owner->scheduledSerial; + + if (active && !set) + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_ACTIVATED); + else if (!active && set) + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_ACTIVATED); + applyState(); + return owner->scheduleConfigure(); +} + +uint32_t CXDGToplevelResource::setSuspeneded(bool sus) { + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_SUSPENDED) != pendingApply.states.end(); + + if (sus == set) + return owner->scheduledSerial; + + if (sus && !set) + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_SUSPENDED); + else if (!sus && set) + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_SUSPENDED); + applyState(); + return owner->scheduleConfigure(); +} + +void CXDGToplevelResource::applyState() { + wl_array arr; + wl_array_init(&arr); + wl_array_add(&arr, pendingApply.states.size() * sizeof(int)); + memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int)); + + resource->sendConfigure(pendingApply.size.x, pendingApply.size.y, &arr); + + wl_array_release(&arr); +} + +void CXDGToplevelResource::close() { + resource->sendClose(); +} + +CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SP owner_, wlr_surface* surface_) : owner(owner_), surface(surface_), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CXdgSurface* r) { + if (mapped) + events.unmap.emit(); + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + resource->setOnDestroy([this](CXdgSurface* r) { + if (mapped) + events.unmap.emit(); + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + + hyprListener_surfaceDestroy.initCallback( + &surface->events.destroy, + [this](void* owner, void* data) { + LOGM(WARN, "wl_surface destroyed before its xdg_surface role object"); + hyprListener_surfaceDestroy.removeCallback(); + hyprListener_surfaceCommit.removeCallback(); + + if (mapped) + events.unmap.emit(); + + mapped = false; + surface = nullptr; + events.destroy.emit(); + }, + nullptr, "CXDGSurfaceResource"); + + hyprListener_surfaceCommit.initCallback( + &surface->events.commit, + [this](void* owner, void* data) { + current = pending; + if (toplevel) + toplevel->current = toplevel->pending; + + if (initialCommit && surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0) { + resource->error(-1, "Buffer attached before initial commit"); + return; + } + + if (surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0 && !mapped) { + // this forces apps to not draw CSD. + if (toplevel) + toplevel->setMaximized(true); + + mapped = true; + wlr_surface_map(surface); + events.map.emit(); + return; + } + + if (surface->pending.buffer_width <= 0 && surface->pending.buffer_height <= 0 && mapped) { + mapped = false; + wlr_surface_unmap(surface); + events.unmap.emit(); + return; + } + + events.commit.emit(); + initialCommit = false; + }, + nullptr, "CXDGSurfaceResource"); + + resource->setGetToplevel([this](CXdgSurface* r, uint32_t id) { + const auto RESOURCE = PROTO::xdgShell->m_vToplevels.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::xdgShell->m_vToplevels.pop_back(); + return; + } + + toplevel = RESOURCE; + toplevel->self = RESOURCE; + + LOGM(LOG, "xdg_surface {:x} gets a toplevel {:x}", (uintptr_t)owner.get(), (uintptr_t)RESOURCE.get()); + + g_pCompositor->m_vWindows.emplace_back(CWindow::create(self.lock())); + + for (auto& p : popups) { + if (!p) + continue; + events.newPopup.emit(p); + } + }); + + resource->setGetPopup([this](CXdgSurface* r, uint32_t id, wl_resource* parentXDG, wl_resource* positionerRes) { + auto parent = parentXDG ? CXDGSurfaceResource::fromResource(parentXDG) : nullptr; + auto positioner = CXDGPositionerResource::fromResource(positionerRes); + const auto RESOURCE = + PROTO::xdgShell->m_vPopups.emplace_back(makeShared(makeShared(r->client(), r->version(), id), parent, self.lock(), positioner)); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::xdgShell->m_vPopups.pop_back(); + return; + } + + popup = RESOURCE; + RESOURCE->self = RESOURCE; + + LOGM(LOG, "xdg_surface {:x} gets a popup {:x} owner {:x}", (uintptr_t)self.get(), (uintptr_t)RESOURCE.get(), (uintptr_t)parent.get()); + + if (!parent) + return; + + parent->popups.emplace_back(RESOURCE); + if (parent->mapped) + parent->events.newPopup.emit(RESOURCE); + }); + + resource->setAckConfigure([this](CXdgSurface* r, uint32_t serial) { + events.ack.emit(serial); + ; // TODO: verify it + }); + + resource->setSetWindowGeometry([this](CXdgSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { + LOGM(LOG, "xdg_surface {:x} requests geometry {}x{} {}x{}", (uintptr_t)this, x, y, w, h); + pending.geometry = {x, y, w, h}; + }); +} + +CXDGSurfaceResource::~CXDGSurfaceResource() { + events.destroy.emit(); + if (configureSource) + wl_event_source_remove(configureSource); +} + +bool CXDGSurfaceResource::good() { + return resource->resource(); +} + +SP CXDGSurfaceResource::fromResource(wl_resource* res) { + auto data = (CXDGSurfaceResource*)(((CXdgSurface*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +static void onConfigure(void* data) { + ((CXDGSurfaceResource*)data)->configure(); +} + +uint32_t CXDGSurfaceResource::scheduleConfigure() { + if (configureSource) + return scheduledSerial; + + configureSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, onConfigure, this); + scheduledSerial = wl_display_next_serial(g_pCompositor->m_sWLDisplay); + + return scheduledSerial; +} + +void CXDGSurfaceResource::configure() { + configureSource = nullptr; + resource->sendConfigure(scheduledSerial); +} + +CXDGPositionerResource::CXDGPositionerResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); }); + resource->setOnDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); }); + + resource->setSetSize([this](CXdgPositioner* r, int32_t x, int32_t y) { + if (x <= 0 || y <= 0) { + r->error(XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid size"); + return; + } + + state.requestedSize = {x, y}; + }); + + resource->setSetAnchorRect([this](CXdgPositioner* r, int32_t x, int32_t y, int32_t w, int32_t h) { + if (w <= 0 || h <= 0) { + r->error(XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid box"); + return; + } + + state.anchorRect = {x, y, w, h}; + }); + + resource->setSetOffset([this](CXdgPositioner* r, int32_t x, int32_t y) { state.offset = {x, y}; }); + + resource->setSetAnchor([this](CXdgPositioner* r, xdgPositionerAnchor a) { state.anchor = a; }); + + resource->setSetGravity([this](CXdgPositioner* r, xdgPositionerGravity g) { state.gravity = g; }); + + resource->setSetConstraintAdjustment([this](CXdgPositioner* r, xdgPositionerConstraintAdjustment a) { state.constraintAdjustment = (uint32_t)a; }); + + // TODO: support this shit better. The current impl _works_, but is lacking and could be wrong in a few cases. + // doesn't matter _that_ much for now, though. +} + +SP CXDGPositionerResource::fromResource(wl_resource* res) { + auto data = (CXDGPositionerResource*)(((CXdgPositioner*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CXDGPositionerResource::good() { + return resource->resource(); +} + +CXDGPositionerRules::CXDGPositionerRules(SP positioner) { + state = positioner->state; +} + +static Vector2D pointForAnchor(const CBox& box, xdgPositionerAnchor anchor) { + switch (anchor) { + case XDG_POSITIONER_ANCHOR_TOP: return box.pos() + Vector2D{box.size().x / 2.F, 0}; + case XDG_POSITIONER_ANCHOR_BOTTOM: return box.pos() + Vector2D{box.size().x / 2.F, box.size().y}; + case XDG_POSITIONER_ANCHOR_LEFT: return box.pos() + Vector2D{0, box.size().y / 2.F}; + case XDG_POSITIONER_ANCHOR_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y / 2.F}; + case XDG_POSITIONER_ANCHOR_TOP_LEFT: return box.pos(); + case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return box.pos() + Vector2D{0, box.size().y}; + case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return box.pos() + Vector2D{box.size().x, 0}; + case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y}; + default: return box.pos(); + } + + return {}; +} + +CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& parentCoord) { + + Debug::log(LOG, "GetPosition with constraint {} {} and parent {}", constraint.pos(), constraint.size(), parentCoord); + + CBox predictedBox = {parentCoord + constraint.pos() + pointForAnchor(state.anchorRect, state.anchor) + state.offset, state.requestedSize}; + + bool success = predictedBox.inside(constraint); + + if (success) + return predictedBox.translate(-parentCoord - constraint.pos()); + + if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y)) { + // attempt to flip + const bool flipX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X; + const bool flipY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y; + + CBox test = predictedBox; + success = true; + if (flipX && test.copy().translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0}).expand(-1).inside(constraint)) + test.translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0}); + else if (flipY && test.copy().translate(Vector2D{0, -predictedBox.h - state.anchorRect.h}).expand(-1).inside(constraint)) + test.translate(Vector2D{0, -predictedBox.h - state.anchorRect.h}); + else if (flipX && flipY && test.copy().translate(Vector2D{-predictedBox.w - state.anchorRect.w, -predictedBox.h - state.anchorRect.h}).expand(-1).inside(constraint)) + test.translate(Vector2D{-predictedBox.w - state.anchorRect.w, -predictedBox.h - state.anchorRect.h}); + else + success = false; + + if (success) + return test.translate(-parentCoord - constraint.pos()); + } + + if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)) { + // attempt to slide + const bool slideX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X; + const bool slideY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y; + + //const bool gravityLeft = state.gravity == XDG_POSITIONER_GRAVITY_NONE || state.gravity == XDG_POSITIONER_GRAVITY_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_TOP_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_BOTTOM_LEFT; + //const bool gravityTop = state.gravity == XDG_POSITIONER_GRAVITY_NONE || state.gravity == XDG_POSITIONER_GRAVITY_TOP || state.gravity == XDG_POSITIONER_GRAVITY_TOP_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_TOP_RIGHT; + + const bool leftEdgeOut = predictedBox.x < constraint.x; + const bool topEdgeOut = predictedBox.y < constraint.y; + const bool rightEdgeOut = predictedBox.x + predictedBox.w > constraint.x + constraint.w; + const bool bottomEdgeOut = predictedBox.y + predictedBox.h > constraint.y + constraint.h; + + CBox test = predictedBox; + + // TODO: this isn't truly conformant. + if (leftEdgeOut && slideX) + test.x = constraint.x; + if (rightEdgeOut && slideX) + test.x = constraint.x + constraint.w - predictedBox.w; + if (topEdgeOut && slideY) + test.y = constraint.y; + if (bottomEdgeOut && slideY) + test.y = constraint.y + constraint.h - predictedBox.y; + + success = test.copy().expand(-1).inside(constraint); + + if (success) + return test.translate(-parentCoord - constraint.pos()); + } + + if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)) { + const bool resizeX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X; + const bool resizeY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y; + + const bool leftEdgeOut = predictedBox.x < constraint.x; + const bool topEdgeOut = predictedBox.y < constraint.y; + const bool rightEdgeOut = predictedBox.x + predictedBox.w > constraint.x + constraint.w; + const bool bottomEdgeOut = predictedBox.y + predictedBox.h > constraint.y + constraint.h; + + CBox test = predictedBox; + + // TODO: this isn't truly conformant. + if (leftEdgeOut && resizeX) { + test.w = test.x + test.w - constraint.x; + test.x = constraint.x; + } + if (rightEdgeOut && resizeX) + test.w = -(constraint.w + constraint.x - test.w - test.x); + if (topEdgeOut && resizeY) { + test.h = test.y + test.h - constraint.y; + test.y = constraint.y; + } + if (bottomEdgeOut && resizeY) + test.h = -(constraint.h + constraint.y - test.h - test.y); + + success = test.copy().expand(-1).inside(constraint); + + if (success) + return test.translate(parentCoord - constraint.pos()); + } + + LOGM(WARN, "Compositor/client bug: xdg_positioner couldn't find a place"); + + return predictedBox.translate(-parentCoord - constraint.pos()); +} + +CXDGWMBase::CXDGWMBase(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); }); + resource->setOnDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); }); + + pClient = resource->client(); + + resource->setCreatePositioner([this](CXdgWmBase* r, uint32_t id) { + const auto RESOURCE = + PROTO::xdgShell->m_vPositioners.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::xdgShell->m_vPositioners.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + positioners.emplace_back(RESOURCE); + + LOGM(LOG, "New xdg_positioner at {:x}", (uintptr_t)RESOURCE.get()); + }); + + resource->setGetXdgSurface([this](CXdgWmBase* r, uint32_t id, wl_resource* surf) { + const auto RESOURCE = PROTO::xdgShell->m_vSurfaces.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), self.lock(), wlr_surface_from_resource(surf))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::xdgShell->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + surfaces.emplace_back(RESOURCE); + + LOGM(LOG, "New xdg_surface at {:x}", (uintptr_t)RESOURCE.get()); + }); +} + +bool CXDGWMBase::good() { + return resource->resource(); +} + +wl_client* CXDGWMBase::client() { + return pClient; +} + +CXDGShellProtocol::CXDGShellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CXDGShellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vWMBases.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vWMBases.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + LOGM(LOG, "New xdg_wm_base at {:x}", (uintptr_t)RESOURCE.get()); +} + +void CXDGShellProtocol::destroyResource(CXDGWMBase* resource) { + std::erase_if(m_vWMBases, [&](const auto& other) { return other.get() == resource; }); +} + +void CXDGShellProtocol::destroyResource(CXDGPositionerResource* resource) { + std::erase_if(m_vPositioners, [&](const auto& other) { return other.get() == resource; }); +} + +void CXDGShellProtocol::destroyResource(CXDGSurfaceResource* resource) { + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); +} + +void CXDGShellProtocol::destroyResource(CXDGToplevelResource* resource) { + std::erase_if(m_vToplevels, [&](const auto& other) { return other.get() == resource; }); +} + +void CXDGShellProtocol::destroyResource(CXDGPopupResource* resource) { + std::erase_if(m_vPopups, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/XDGShell.hpp b/src/protocols/XDGShell.hpp new file mode 100644 index 00000000..e214b6ac --- /dev/null +++ b/src/protocols/XDGShell.hpp @@ -0,0 +1,256 @@ +#pragma once + +#include +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "xdg-shell.hpp" +#include "../helpers/Vector2D.hpp" +#include "../helpers/Box.hpp" +#include "../helpers/signal/Signal.hpp" + +class CXDGWMBase; +class CXDGPositionerResource; +class CXDGSurfaceResource; +class CXDGToplevelResource; +class CXDGPopupResource; +class CSeatGrab; + +struct SXDGPositionerState { + Vector2D requestedSize; + CBox anchorRect; + xdgPositionerAnchor anchor = XDG_POSITIONER_ANCHOR_NONE; + xdgPositionerGravity gravity = XDG_POSITIONER_GRAVITY_NONE; + uint32_t constraintAdjustment = 0; + Vector2D offset; + bool reactive = false; + Vector2D parentSize; +}; + +class CXDGPositionerRules { + public: + CXDGPositionerRules(SP positioner); + + CBox getPosition(const CBox& constraint, const Vector2D& parentPos); + + private: + SXDGPositionerState state; +}; + +class CXDGPopupResource { + public: + CXDGPopupResource(SP resource_, SP parent_, SP surface_, SP positioner_); + ~CXDGPopupResource(); + + static SP fromResource(wl_resource*); + + bool good(); + + void applyPositioning(const CBox& availableBox, const Vector2D& t1coord /* relative to box */); + + WP surface; + WP parent; + WP self; + + bool taken = false; + + CBox geometry; + + struct { + CSignal reposition; + CSignal destroy; // only the role + } events; + + // schedules a configure event + void configure(const CBox& box); + + void done(); + void repositioned(); + + private: + SP resource; + + uint32_t lastRepositionToken = 0; + + Vector2D accumulateParentOffset(); + + CXDGPositionerRules positionerRules; +}; + +class CXDGToplevelResource { + public: + CXDGToplevelResource(SP resource_, SP owner_); + ~CXDGToplevelResource(); + + static SP fromResource(wl_resource*); + + WP owner; + WP self; + + PHLWINDOWREF window; + + bool good(); + + // schedule a configure event + uint32_t setSize(const Vector2D& size); + uint32_t setMaximized(bool maximized); + uint32_t setFullscreen(bool fullscreen); + uint32_t setActive(bool active); + uint32_t setSuspeneded(bool sus); + + void close(); + + struct { + CSignal sizeLimitsChanged; + CSignal stateChanged; // maximized, fs, minimized, etc. + CSignal metadataChanged; // title, appid + CSignal destroy; // only the role + } events; + + struct { + std::string title; + std::string appid; + + // volatile state: is reset after the stateChanged signal fires + std::optional requestsMaximize; + std::optional requestsFullscreen; + std::optional requestsMinimize; + } state; + + struct { + Vector2D size; + std::vector states; + } pendingApply; + + struct { + Vector2D minSize = {1, 1}; + Vector2D maxSize = {1337420, 694200}; + } pending, current; + + WP parent; + + private: + SP resource; + void applyState(); +}; + +class CXDGSurfaceResource { + public: + CXDGSurfaceResource(SP resource_, SP owner_, wlr_surface* surface_); + ~CXDGSurfaceResource(); + + static SP fromResource(wl_resource*); + + bool good(); + + WP owner; + wlr_surface* surface = nullptr; + + WP toplevel; + WP popup; + + WP self; + + struct { + CBox geometry; + } pending, current; + + struct { + CSignal ack; + CSignal commit; + CSignal map; + CSignal unmap; + CSignal destroy; + CSignal newPopup; // SP + } events; + + bool initialCommit = true; + bool mapped = false; + + uint32_t scheduleConfigure(); + // do not call directly + void configure(); + + private: + SP resource; + + uint32_t lastConfigureSerial = 0; + uint32_t scheduledSerial = 0; + + wl_event_source* configureSource = nullptr; + + // + std::vector> popups; + + DYNLISTENER(surfaceDestroy); + DYNLISTENER(surfaceCommit); + + friend class CXDGPopupResource; + friend class CXDGToplevelResource; +}; + +class CXDGPositionerResource { + public: + CXDGPositionerResource(SP resource_, SP owner_); + + static SP fromResource(wl_resource*); + + bool good(); + + SXDGPositionerState state; + + WP owner; + WP self; + + private: + SP resource; +}; + +class CXDGWMBase { + public: + CXDGWMBase(SP resource_); + + bool good(); + wl_client* client(); + + std::vector> positioners; + std::vector> surfaces; + + WP self; + + private: + SP resource; + wl_client* pClient = nullptr; +}; + +class CXDGShellProtocol : public IWaylandProtocol { + public: + CXDGShellProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + private: + void destroyResource(CXDGWMBase* resource); + void destroyResource(CXDGPositionerResource* resource); + void destroyResource(CXDGSurfaceResource* resource); + void destroyResource(CXDGToplevelResource* resource); + void destroyResource(CXDGPopupResource* resource); + + // + std::vector> m_vWMBases; + std::vector> m_vPositioners; + std::vector> m_vSurfaces; + std::vector> m_vToplevels; + std::vector> m_vPopups; + + friend class CXDGWMBase; + friend class CXDGPositionerResource; + friend class CXDGSurfaceResource; + friend class CXDGToplevelResource; + friend class CXDGPopupResource; +}; + +namespace PROTO { + inline UP xdgShell; +}; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 38dba439..1aa246fb 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -10,6 +10,7 @@ #include "../desktop/LayerSurface.hpp" #include "../protocols/SessionLock.hpp" #include "../protocols/LayerShell.hpp" +#include "../protocols/XDGShell.hpp" #include "../protocols/PresentationTime.hpp" extern "C" { @@ -615,9 +616,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec if (mode == RENDER_PASS_ALL || mode == RENDER_PASS_POPUP) { if (!pWindow->m_bIsX11) { - CBox geom; - wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, geom.pWlr()); - geom.applyFromWlr(); + CBox geom = pWindow->m_pXDGSurface->current.geometry; renderdata.x -= geom.x; renderdata.y -= geom.y; @@ -643,7 +642,20 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec if (pWindow->m_sAdditionalConfigData.nearestNeighbor.toUnderlying()) g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true; - wlr_xdg_surface_for_each_popup_surface(pWindow->m_uSurface.xdg, renderSurface, &renderdata); + pWindow->m_pPopupHead->breadthfirst( + [](CPopup* popup, void* data) { + if (!popup->m_sWLSurface.wlr()) + return; + auto pos = popup->coordsRelativeToParent(); + auto rd = (SRenderData*)data; + Vector2D oldPos = {rd->x, rd->y}; + rd->x += pos.x; + rd->y += pos.y; + wlr_surface_for_each_surface(popup->m_sWLSurface.wlr(), renderSurface, rd); + rd->x = oldPos.x; + rd->y = oldPos.y; + }, + &renderdata); g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false; @@ -1000,9 +1012,7 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, wlr_surface* pSurfa if (!main || !pWindow) return; - CBox geom; - wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, geom.pWlr()); - geom.applyFromWlr(); + CBox geom = pWindow->m_pXDGSurface->current.geometry; // ignore X and Y, adjust uv if (geom.x != 0 || geom.y != 0 || geom.width > pWindow->m_vRealSize.value().x || geom.height > pWindow->m_vRealSize.value().y) { @@ -2530,8 +2540,7 @@ void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) { if (PCANDIDATE->m_bIsX11) { surfaceCount = 1; } else { - wlr_xdg_surface_for_each_surface(PCANDIDATE->m_uSurface.xdg, countSubsurfacesIter, &surfaceCount); - wlr_xdg_surface_for_each_popup_surface(PCANDIDATE->m_uSurface.xdg, countSubsurfacesIter, &surfaceCount); + surfaceCount = PCANDIDATE->popupsCount() + PCANDIDATE->surfacesCount(); } if (surfaceCount > 1)