desktop: cleanup, unify desktop elements as views (#12563)

This commit is contained in:
Vaxry 2025-12-08 15:04:40 +00:00 committed by GitHub
parent 834f019bab
commit 920353370b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
105 changed files with 2636 additions and 2337 deletions

View file

@ -0,0 +1,82 @@
#include "GlobalViewMethods.hpp"
#include "../../Compositor.hpp"
#include "LayerSurface.hpp"
#include "Window.hpp"
#include "Popup.hpp"
#include "Subsurface.hpp"
#include "SessionLock.hpp"
#include "../../protocols/core/Compositor.hpp"
#include "../../protocols/core/Subcompositor.hpp"
#include "../../protocols/SessionLock.hpp"
using namespace Desktop;
using namespace Desktop::View;
std::vector<SP<IView>> View::getViewsForWorkspace(PHLWORKSPACE ws) {
std::vector<SP<IView>> views;
for (const auto& w : g_pCompositor->m_windows) {
if (!w->visible() || w->m_workspace != ws)
continue;
views.emplace_back(w);
w->wlSurface()->resource()->breadthfirst(
[&views](SP<CWLSurfaceResource> s, const Vector2D& pos, void* data) {
auto surf = CWLSurface::fromResource(s);
if (!surf || !s->m_mapped)
return;
views.emplace_back(surf->view());
},
nullptr);
// xwl windows dont have this
if (w->m_popupHead) {
w->m_popupHead->breadthfirst(
[&views](SP<CPopup> s, void* data) {
auto surf = s->wlSurface();
if (!surf || !s->visible())
return;
views.emplace_back(surf->view());
},
nullptr);
}
}
for (const auto& l : g_pCompositor->m_layers) {
if (!l->visible() || l->m_monitor != ws->m_monitor)
continue;
views.emplace_back(l);
l->m_popupHead->breadthfirst(
[&views](SP<CPopup> p, void* data) {
auto surf = p->wlSurface();
if (!surf || !p->visible())
return;
views.emplace_back(surf->view());
},
nullptr);
}
for (const auto& v : g_pCompositor->m_otherViews) {
if (!v->visible() || !v->desktopComponent())
continue;
if (v->type() == VIEW_TYPE_LOCK_SCREEN) {
const auto LOCK = Desktop::View::CSessionLock::fromView(v);
if (LOCK->monitor() != ws->m_monitor)
continue;
views.emplace_back(LOCK);
continue;
}
}
return views;
}

View file

@ -0,0 +1,11 @@
#pragma once
#include "View.hpp"
#include "../Workspace.hpp"
#include <vector>
namespace Desktop::View {
std::vector<SP<IView>> getViewsForWorkspace(PHLWORKSPACE ws);
};

View file

@ -0,0 +1,462 @@
#include "LayerSurface.hpp"
#include "../state/FocusState.hpp"
#include "../../Compositor.hpp"
#include "../../protocols/LayerShell.hpp"
#include "../../protocols/core/Compositor.hpp"
#include "../../managers/SeatManager.hpp"
#include "../../managers/animation/AnimationManager.hpp"
#include "../../managers/animation/DesktopAnimationManager.hpp"
#include "../../render/Renderer.hpp"
#include "../../config/ConfigManager.hpp"
#include "../../helpers/Monitor.hpp"
#include "../../managers/input/InputManager.hpp"
#include "../../managers/HookSystemManager.hpp"
#include "../../managers/EventManager.hpp"
using namespace Desktop;
using namespace Desktop::View;
PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
PHLLS pLS = SP<CLayerSurface>(new CLayerSurface(resource));
auto pMonitor = resource->m_monitor.empty() ? Desktop::focusState()->monitor() : g_pCompositor->getMonitorFromName(resource->m_monitor);
pLS->m_wlSurface->assign(resource->m_surface.lock(), pLS);
if (!pMonitor) {
Debug::log(ERR, "New LS has no monitor??");
return pLS;
}
if (pMonitor->m_mirrorOf)
pMonitor = g_pCompositor->m_monitors.front();
pLS->m_self = pLS;
pLS->m_namespace = resource->m_layerNamespace;
pLS->m_layer = resource->m_current.layer;
pLS->m_popupHead = CPopup::create(pLS);
pLS->m_monitor = pMonitor;
pMonitor->m_layerSurfaceLayers[resource->m_current.layer].emplace_back(pLS);
pLS->m_ruleApplicator = makeUnique<Desktop::Rule::CLayerRuleApplicator>(pLS);
g_pAnimationManager->createAnimation(0.f, pLS->m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realSize, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
pLS->registerCallbacks();
pLS->m_alpha->setValueAndWarp(0.f);
Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", rc<uintptr_t>(resource.get()), resource->m_layerNamespace, sc<int>(pLS->m_layer),
pMonitor->m_name);
return pLS;
}
PHLLS CLayerSurface::fromView(SP<IView> v) {
if (!v || v->type() != VIEW_TYPE_LAYER_SURFACE)
return nullptr;
return dynamicPointerCast<CLayerSurface>(v);
}
void CLayerSurface::registerCallbacks() {
m_alpha->setUpdateCallback([this](auto) {
if (m_ruleApplicator->dimAround().valueOrDefault() && m_monitor)
g_pHyprRenderer->damageMonitor(m_monitor.lock());
});
}
CLayerSurface::CLayerSurface(SP<CLayerShellResource> resource_) : IView(CWLSurface::create()), m_layerSurface(resource_) {
m_listeners.commit = m_layerSurface->m_events.commit.listen([this] { onCommit(); });
m_listeners.map = m_layerSurface->m_events.map.listen([this] { onMap(); });
m_listeners.unmap = m_layerSurface->m_events.unmap.listen([this] { onUnmap(); });
m_listeners.destroy = m_layerSurface->m_events.destroy.listen([this] { onDestroy(); });
}
CLayerSurface::~CLayerSurface() {
if (!g_pHyprOpenGL)
return;
if (m_wlSurface)
m_wlSurface->unassign();
g_pHyprRenderer->makeEGLCurrent();
std::erase_if(g_pHyprOpenGL->m_layerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == m_self.lock(); });
for (auto const& mon : g_pCompositor->m_realMonitors) {
for (auto& lsl : mon->m_layerSurfaceLayers) {
std::erase_if(lsl, [this](auto& ls) { return ls.expired() || ls.get() == this; });
}
}
}
eViewType CLayerSurface::type() const {
return VIEW_TYPE_LAYER_SURFACE;
}
bool CLayerSurface::visible() const {
return (m_mapped && m_layerSurface && m_layerSurface->m_mapped && m_wlSurface && m_wlSurface->resource()) || (m_fadingOut && m_alpha->value() > 0.F);
}
std::optional<CBox> CLayerSurface::logicalBox() const {
return surfaceLogicalBox();
}
std::optional<CBox> CLayerSurface::surfaceLogicalBox() const {
if (!visible())
return std::nullopt;
return CBox{m_realPosition->value(), m_realSize->value()};
}
bool CLayerSurface::desktopComponent() const {
return true;
}
void CLayerSurface::onDestroy() {
Debug::log(LOG, "LayerSurface {:x} destroyed", rc<uintptr_t>(m_layerSurface.get()));
const auto PMONITOR = m_monitor.lock();
if (!PMONITOR)
Debug::log(WARN, "Layersurface destroyed on an invalid monitor (removed?)");
if (!m_fadingOut) {
if (m_mapped) {
Debug::log(LOG, "Forcing an unmap of a LS that did a straight destroy!");
onUnmap();
} else {
Debug::log(LOG, "Removing LayerSurface that wasn't mapped.");
if (m_alpha)
g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT);
m_fadingOut = true;
g_pCompositor->addToFadingOutSafe(m_self.lock());
}
}
m_popupHead.reset();
m_noProcess = true;
// rearrange to fix the reserved areas
if (PMONITOR) {
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
PMONITOR->m_scheduledRecalc = true;
// and damage
CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height};
g_pHyprRenderer->damageBox(geomFixed);
}
m_readyToDelete = true;
m_layerSurface.reset();
if (m_wlSurface)
m_wlSurface->unassign();
m_listeners.unmap.reset();
m_listeners.destroy.reset();
m_listeners.map.reset();
m_listeners.commit.reset();
}
void CLayerSurface::onMap() {
Debug::log(LOG, "LayerSurface {:x} mapped", rc<uintptr_t>(m_layerSurface.get()));
m_mapped = true;
m_interactivity = m_layerSurface->m_current.interactivity;
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ALL);
m_layerSurface->m_surface->map();
// this layer might be re-mapped.
m_fadingOut = false;
g_pCompositor->removeFromFadingOutSafe(m_self.lock());
// fix if it changed its mon
const auto PMONITOR = m_monitor.lock();
if (!PMONITOR)
return;
PMONITOR->m_scheduledRecalc = true;
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
m_wlSurface->resource()->enter(PMONITOR->m_self.lock());
const bool ISEXCLUSIVE = m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
if (ISEXCLUSIVE)
g_pInputManager->m_exclusiveLSes.push_back(m_self);
const bool GRABSFOCUS = ISEXCLUSIVE ||
(m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained
(g_pSeatManager->m_mouse.expired() || !g_pInputManager->isConstrained()));
if (GRABSFOCUS) {
// TODO: use the new superb really very cool grab
if (g_pSeatManager->m_seatGrab && !g_pSeatManager->m_seatGrab->accepts(m_wlSurface->resource()))
g_pSeatManager->setGrab(nullptr);
g_pInputManager->releaseAllMouseButtons();
Desktop::focusState()->rawSurfaceFocus(m_wlSurface->resource());
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y);
g_pSeatManager->setPointerFocus(m_wlSurface->resource(), LOCAL);
g_pInputManager->m_emptyFocusCursorSet = false;
}
m_position = Vector2D(m_geometry.x, m_geometry.y);
CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height};
g_pHyprRenderer->damageBox(geomFixed);
g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_IN);
m_readyToDelete = false;
m_fadingOut = false;
g_pEventManager->postEvent(SHyprIPCEvent{.event = "openlayer", .data = m_namespace});
EMIT_HOOK_EVENT("openLayer", m_self.lock());
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), PMONITOR->m_scale);
g_pCompositor->setPreferredTransformForSurface(m_wlSurface->resource(), PMONITOR->m_transform);
}
void CLayerSurface::onUnmap() {
Debug::log(LOG, "LayerSurface {:x} unmapped", rc<uintptr_t>(m_layerSurface.get()));
g_pEventManager->postEvent(SHyprIPCEvent{.event = "closelayer", .data = m_layerSurface->m_layerNamespace});
EMIT_HOOK_EVENT("closeLayer", m_self.lock());
std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other || other == m_self; });
if (!m_monitor || g_pCompositor->m_unsafeState) {
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");
g_pCompositor->addToFadingOutSafe(m_self.lock());
m_mapped = false;
if (m_layerSurface && m_layerSurface->m_surface)
m_layerSurface->m_surface->unmap();
g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT);
return;
}
// end any pending animations so that snapshot has right dimensions
m_realPosition->warp();
m_realSize->warp();
// make a snapshot and start fade
g_pHyprRenderer->makeSnapshot(m_self.lock());
g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT);
m_fadingOut = true;
m_mapped = false;
if (m_layerSurface && m_layerSurface->m_surface)
m_layerSurface->m_surface->unmap();
g_pCompositor->addToFadingOutSafe(m_self.lock());
const auto PMONITOR = m_monitor.lock();
const bool WASLASTFOCUS = g_pSeatManager->m_state.keyboardFocus == m_wlSurface->resource() || g_pSeatManager->m_state.pointerFocus == m_wlSurface->resource();
if (!PMONITOR)
return;
// refocus if needed
// vvvvvvvvvvvvv if there is a last focus and the last focus is not keyboard focusable, fallback to window
if (WASLASTFOCUS ||
(Desktop::focusState()->surface() && Desktop::focusState()->surface()->m_hlSurface && !Desktop::focusState()->surface()->m_hlSurface->keyboardFocusable())) {
if (!g_pInputManager->refocusLastWindow(PMONITOR))
g_pInputManager->refocus();
} else if (Desktop::focusState()->surface() && Desktop::focusState()->surface() != m_wlSurface->resource())
g_pSeatManager->setKeyboardFocus(Desktop::focusState()->surface());
CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height};
g_pHyprRenderer->damageBox(geomFixed);
geomFixed = {m_geometry.x + sc<int>(PMONITOR->m_position.x), m_geometry.y + sc<int>(PMONITOR->m_position.y), sc<int>(m_layerSurface->m_surface->m_current.size.x),
sc<int>(m_layerSurface->m_surface->m_current.size.y)};
g_pHyprRenderer->damageBox(geomFixed);
g_pInputManager->simulateMouseMovement();
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
}
void CLayerSurface::onCommit() {
if (!m_layerSurface)
return;
if (!m_mapped) {
// we're re-mapping if this is the case
if (m_layerSurface->m_surface && !m_layerSurface->m_surface->m_current.texture) {
m_fadingOut = false;
m_geometry = {};
g_pHyprRenderer->arrangeLayersForMonitor(monitorID());
}
return;
}
const auto PMONITOR = m_monitor.lock();
if (!PMONITOR)
return;
if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
CBox geomFixed = {m_geometry.x, m_geometry.y, m_geometry.width, m_geometry.height};
g_pHyprRenderer->damageBox(geomFixed);
if (m_layerSurface->m_current.committed != 0) {
if (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_LAYER && m_layerSurface->m_current.layer != m_layer) {
for (auto it = PMONITOR->m_layerSurfaceLayers[m_layer].begin(); it != PMONITOR->m_layerSurfaceLayers[m_layer].end(); it++) {
if (*it == m_self) {
PMONITOR->m_layerSurfaceLayers[m_layerSurface->m_current.layer].emplace_back(*it);
PMONITOR->m_layerSurfaceLayers[m_layer].erase(it);
break;
}
}
// update alpha when window is in fullscreen
auto PWORKSPACE = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace;
if (PWORKSPACE && PWORKSPACE->m_fullscreenMode == FSMODE_FULLSCREEN) {
// warp if switching render layer so we don't see glitches and have clean fade
if ((m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) &&
(m_layerSurface->m_current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP || m_layerSurface->m_current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY))
m_alpha->setValueAndWarp(0.f);
// from overlay to top
if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY && m_layerSurface->m_current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)
*m_alpha = 0.f;
// to overlay
if (m_layerSurface->m_current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY)
*m_alpha = 1.f;
}
m_layer = m_layerSurface->m_current.layer;
if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
}
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
PMONITOR->m_scheduledRecalc = true;
} else {
m_position = Vector2D(m_geometry.x, m_geometry.y);
// update geom if it changed
if (m_layerSurface->m_surface->m_current.scale == 1 && PMONITOR->m_scale != 1.f && m_layerSurface->m_surface->m_current.viewport.hasDestination) {
// fractional scaling. Dirty hack.
m_geometry = {m_geometry.pos(), m_layerSurface->m_surface->m_current.viewport.destination};
} else {
// this is because some apps like e.g. rofi-lbonn can't fucking use the protocol correctly.
m_geometry = {m_geometry.pos(), m_layerSurface->m_surface->m_current.size};
}
}
if (m_realPosition->goal() != m_geometry.pos()) {
if (m_realPosition->isBeingAnimated())
*m_realPosition = m_geometry.pos();
else
m_realPosition->setValueAndWarp(m_geometry.pos());
}
if (m_realSize->goal() != m_geometry.size()) {
if (m_realSize->isBeingAnimated())
*m_realSize = m_geometry.size();
else
m_realSize->setValueAndWarp(m_geometry.size());
}
if (m_mapped && (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_INTERACTIVITY)) {
bool WASLASTFOCUS = false;
m_layerSurface->m_surface->breadthfirst(
[&WASLASTFOCUS](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) { WASLASTFOCUS = WASLASTFOCUS || g_pSeatManager->m_state.keyboardFocus == surf; },
nullptr);
if (!WASLASTFOCUS && m_popupHead) {
m_popupHead->breadthfirst(
[&WASLASTFOCUS](WP<Desktop::View::CPopup> popup, void* data) {
WASLASTFOCUS = WASLASTFOCUS || (popup->wlSurface() && g_pSeatManager->m_state.keyboardFocus == popup->wlSurface()->resource());
},
nullptr);
}
const bool WASEXCLUSIVE = m_interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
const bool ISEXCLUSIVE = m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
if (!WASEXCLUSIVE && ISEXCLUSIVE)
g_pInputManager->m_exclusiveLSes.push_back(m_self);
else if (WASEXCLUSIVE && !ISEXCLUSIVE)
std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == m_self.lock(); });
// if the surface was focused and interactive but now isn't, refocus
if (WASLASTFOCUS && m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) {
// moveMouseUnified won't focus non interactive layers but it won't unfocus them either,
// so unfocus the surface here.
Desktop::focusState()->rawSurfaceFocus(nullptr);
g_pInputManager->refocusLastWindow(m_monitor.lock());
} else if (WASLASTFOCUS && WASEXCLUSIVE && m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) {
g_pInputManager->simulateMouseMovement();
} else if (!WASEXCLUSIVE && ISEXCLUSIVE) {
// if now exclusive and not previously
g_pSeatManager->setGrab(nullptr);
g_pInputManager->releaseAllMouseButtons();
Desktop::focusState()->rawSurfaceFocus(m_wlSurface->resource());
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y);
g_pSeatManager->setPointerFocus(m_wlSurface->resource(), LOCAL);
g_pInputManager->m_emptyFocusCursorSet = false;
}
}
m_interactivity = m_layerSurface->m_current.interactivity;
g_pHyprRenderer->damageSurface(m_wlSurface->resource(), m_position.x, m_position.y);
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), PMONITOR->m_scale);
g_pCompositor->setPreferredTransformForSurface(m_wlSurface->resource(), PMONITOR->m_transform);
}
bool CLayerSurface::isFadedOut() {
if (!m_fadingOut)
return false;
return !m_realPosition->isBeingAnimated() && !m_realSize->isBeingAnimated() && !m_alpha->isBeingAnimated();
}
int CLayerSurface::popupsCount() {
if (!m_layerSurface || !m_mapped || m_fadingOut)
return 0;
int no = -1; // we have one dummy
m_popupHead->breadthfirst([](WP<Desktop::View::CPopup> p, void* data) { *sc<int*>(data) += 1; }, &no);
return no;
}
MONITORID CLayerSurface::monitorID() {
return m_monitor ? m_monitor->m_id : MONITOR_INVALID;
}
pid_t CLayerSurface::getPID() {
pid_t PID = -1;
if (!m_layerSurface || !m_layerSurface->m_surface || !m_layerSurface->m_surface->getResource() || !m_layerSurface->m_surface->getResource()->resource() ||
!m_layerSurface->m_surface->getResource()->client())
return -1;
wl_client_get_credentials(m_layerSurface->m_surface->getResource()->client(), &PID, nullptr, nullptr);
return PID;
}

View file

@ -0,0 +1,105 @@
#pragma once
#include <string>
#include "../../defines.hpp"
#include "WLSurface.hpp"
#include "View.hpp"
#include "../rule/layerRule/LayerRuleApplicator.hpp"
#include "../../helpers/AnimatedVariable.hpp"
class CLayerShellResource;
namespace Desktop::View {
class CLayerSurface : public IView {
public:
static PHLLS create(SP<CLayerShellResource>);
static PHLLS fromView(SP<IView>);
private:
CLayerSurface(SP<CLayerShellResource>);
public:
virtual ~CLayerSurface();
virtual eViewType type() const;
virtual bool visible() const;
virtual std::optional<CBox> logicalBox() const;
virtual bool desktopComponent() const;
virtual std::optional<CBox> surfaceLogicalBox() const;
bool isFadedOut();
int popupsCount();
PHLANIMVAR<Vector2D> m_realPosition;
PHLANIMVAR<Vector2D> m_realSize;
PHLANIMVAR<float> m_alpha;
WP<CLayerShellResource> m_layerSurface;
// the header providing the enum type cannot be imported here
int m_interactivity = 0;
bool m_mapped = false;
uint32_t m_layer = 0;
PHLMONITORREF m_monitor;
bool m_fadingOut = false;
bool m_readyToDelete = false;
bool m_noProcess = false;
UP<Desktop::Rule::CLayerRuleApplicator> m_ruleApplicator;
PHLLSREF m_self;
CBox m_geometry = {0, 0, 0, 0};
Vector2D m_position;
std::string m_namespace = "";
SP<Desktop::View::CPopup> m_popupHead;
pid_t getPID();
void onDestroy();
void onMap();
void onUnmap();
void onCommit();
MONITORID monitorID();
private:
struct {
CHyprSignalListener destroy;
CHyprSignalListener map;
CHyprSignalListener unmap;
CHyprSignalListener commit;
} m_listeners;
void registerCallbacks();
// For the list lookup
bool operator==(const CLayerSurface& rhs) const {
return m_layerSurface == rhs.m_layerSurface && m_monitor == rhs.m_monitor;
}
};
inline bool valid(PHLLS l) {
return l;
}
inline bool valid(PHLLSREF l) {
return l;
}
inline bool validMapped(PHLLS l) {
if (!valid(l))
return false;
return l->visible();
}
inline bool validMapped(PHLLSREF l) {
if (!valid(l))
return false;
return l->visible();
}
}

485
src/desktop/view/Popup.cpp Normal file
View file

@ -0,0 +1,485 @@
#include "Popup.hpp"
#include "../../config/ConfigValue.hpp"
#include "../../config/ConfigManager.hpp"
#include "../../Compositor.hpp"
#include "../../protocols/LayerShell.hpp"
#include "../../protocols/XDGShell.hpp"
#include "../../protocols/core/Compositor.hpp"
#include "../../managers/SeatManager.hpp"
#include "../../managers/animation/AnimationManager.hpp"
#include "LayerSurface.hpp"
#include "../../managers/input/InputManager.hpp"
#include "../../render/Renderer.hpp"
#include "../../render/OpenGL.hpp"
#include <ranges>
using namespace Desktop;
using namespace Desktop::View;
SP<CPopup> CPopup::create(PHLWINDOW pOwner) {
auto popup = SP<CPopup>(new CPopup());
popup->m_windowOwner = pOwner;
popup->m_self = popup;
popup->initAllSignals();
return popup;
}
SP<CPopup> CPopup::create(PHLLS pOwner) {
auto popup = SP<CPopup>(new CPopup());
popup->m_layerOwner = pOwner;
popup->m_self = popup;
popup->initAllSignals();
return popup;
}
SP<CPopup> CPopup::create(SP<CXDGPopupResource> resource, WP<CPopup> pOwner) {
auto popup = SP<CPopup>(new CPopup());
popup->m_resource = resource;
popup->m_windowOwner = pOwner->m_windowOwner;
popup->m_layerOwner = pOwner->m_layerOwner;
popup->m_parent = pOwner;
popup->m_self = popup;
popup->wlSurface()->assign(resource->m_surface->m_surface.lock(), popup);
popup->m_lastSize = resource->m_surface->m_current.geometry.size();
popup->reposition();
popup->initAllSignals();
return popup;
}
SP<CPopup> CPopup::fromView(SP<IView> v) {
if (!v || v->type() != VIEW_TYPE_POPUP)
return nullptr;
return dynamicPointerCast<CPopup>(v);
}
CPopup::CPopup() : IView(CWLSurface::create()) {
;
}
CPopup::~CPopup() {
if (m_wlSurface)
m_wlSurface->unassign();
}
eViewType CPopup::type() const {
return VIEW_TYPE_POPUP;
}
bool CPopup::visible() const {
if (!m_mapped || !m_wlSurface->resource())
return false;
if (!m_windowOwner.expired())
return g_pHyprRenderer->shouldRenderWindow(m_windowOwner.lock());
if (!m_layerOwner.expired())
return true;
if (m_parent)
return m_parent->visible();
return false;
}
std::optional<CBox> CPopup::logicalBox() const {
return surfaceLogicalBox();
}
std::optional<CBox> CPopup::surfaceLogicalBox() const {
if (!visible())
return std::nullopt;
return CBox{t1ParentCoords(), size()};
}
bool CPopup::desktopComponent() const {
return true;
}
void CPopup::initAllSignals() {
g_pAnimationManager->createAnimation(0.f, m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadePopupsIn"), AVARDAMAGE_NONE);
m_alpha->setUpdateCallback([this](auto) {
//
g_pHyprRenderer->damageBox(CBox{coordsGlobal(), size()});
});
m_alpha->setCallbackOnEnd(
[this](auto) {
if (inert()) {
g_pHyprRenderer->damageBox(CBox{coordsGlobal(), size()});
fullyDestroy();
}
},
false);
if (!m_resource) {
if (!m_windowOwner.expired())
m_listeners.newPopup = m_windowOwner->m_xdgSurface->m_events.newPopup.listen([this](const auto& resource) { this->onNewPopup(resource); });
else if (!m_layerOwner.expired())
m_listeners.newPopup = m_layerOwner->m_layerSurface->m_events.newPopup.listen([this](const auto& resource) { this->onNewPopup(resource); });
else
ASSERT(false);
return;
}
m_listeners.reposition = m_resource->m_events.reposition.listen([this] { this->onReposition(); });
m_listeners.map = m_resource->m_surface->m_events.map.listen([this] { this->onMap(); });
m_listeners.unmap = m_resource->m_surface->m_events.unmap.listen([this] { this->onUnmap(); });
m_listeners.dismissed = m_resource->m_events.dismissed.listen([this] { this->onUnmap(); });
m_listeners.destroy = m_resource->m_surface->m_events.destroy.listen([this] { this->onDestroy(); });
m_listeners.commit = m_resource->m_surface->m_events.commit.listen([this] { this->onCommit(); });
m_listeners.newPopup = m_resource->m_surface->m_events.newPopup.listen([this](const auto& resource) { this->onNewPopup(resource); });
}
void CPopup::onNewPopup(SP<CXDGPopupResource> popup) {
const auto& POPUP = m_children.emplace_back(CPopup::create(popup, m_self));
POPUP->m_self = POPUP;
Debug::log(LOG, "New popup at {:x}", rc<uintptr_t>(this));
}
void CPopup::onDestroy() {
m_inert = true;
if (!m_parent)
return; // head node
m_subsurfaceHead.reset();
m_children.clear();
m_wlSurface.reset();
if (m_fadingOut && m_alpha->isBeingAnimated()) {
Debug::log(LOG, "popup {:x}: skipping full destroy, animating", rc<uintptr_t>(this));
return;
}
fullyDestroy();
}
void CPopup::fullyDestroy() {
Debug::log(LOG, "popup {:x} fully destroying", rc<uintptr_t>(this));
g_pHyprRenderer->makeEGLCurrent();
std::erase_if(g_pHyprOpenGL->m_popupFramebuffers, [&](const auto& other) { return other.first.expired() || other.first == m_self; });
std::erase_if(m_parent->m_children, [this](const auto& other) { return other.get() == this; });
}
void CPopup::onMap() {
if (m_mapped)
return;
m_mapped = true;
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
const auto COORDS = coordsGlobal();
const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS);
CBox box = m_wlSurface->resource()->extends();
box.translate(COORDS).expand(4);
g_pHyprRenderer->damageBox(box);
m_lastPos = coordsRelativeToParent();
g_pInputManager->simulateMouseMovement();
m_subsurfaceHead = CSubsurface::create(m_self);
//unconstrain();
sendScale();
m_resource->m_surface->m_surface->enter(PMONITOR->m_self.lock());
if (!m_layerOwner.expired() && m_layerOwner->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_layerOwner->m_layer));
m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadePopupsIn"));
m_alpha->setValueAndWarp(0.F);
*m_alpha = 1.F;
Debug::log(LOG, "popup {:x}: mapped", rc<uintptr_t>(this));
}
void CPopup::onUnmap() {
if (!m_mapped)
return;
if (!m_resource || !m_resource->m_surface) {
Debug::log(ERR, "CPopup: orphaned (no surface/resource) and unmaps??");
onDestroy();
return;
}
Debug::log(LOG, "popup {:x}: unmapped", rc<uintptr_t>(this));
// if the popup committed a different size right now, we also need to damage the old size.
const Vector2D MAX_DAMAGE_SIZE = {std::max(m_lastSize.x, m_resource->m_surface->m_surface->m_current.size.x),
std::max(m_lastSize.y, m_resource->m_surface->m_surface->m_current.size.y)};
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
m_lastPos = coordsRelativeToParent();
const auto COORDS = coordsGlobal();
CBox box = m_wlSurface->resource()->extends();
box.translate(COORDS).expand(4);
g_pHyprRenderer->damageBox(box);
// damage the last popup's explicit max size as well
box = CBox{COORDS, MAX_DAMAGE_SIZE}.expand(4);
g_pHyprRenderer->damageBox(box);
m_lastSize = MAX_DAMAGE_SIZE;
g_pHyprRenderer->makeSnapshot(m_self);
m_fadingOut = true;
m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadePopupsOut"));
m_alpha->setValueAndWarp(1.F);
*m_alpha = 0.F;
m_mapped = false;
m_subsurfaceHead.reset();
if (!m_layerOwner.expired() && m_layerOwner->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_layerOwner->m_layer));
// damage all children
breadthfirst(
[](WP<CPopup> p, void* data) {
if (!p->m_resource)
return;
auto box = CBox{p->coordsGlobal(), p->size()};
g_pHyprRenderer->damageBox(box);
},
nullptr);
// TODO: probably refocus, but without a motion event?
// const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == m_pWLSurface->resource() || g_pSeatManager->state.pointerFocus == m_pWLSurface->resource();
// if (WASLASTFOCUS)
// g_pInputManager->simulateMouseMovement();
}
void CPopup::onCommit(bool ignoreSiblings) {
if (!m_resource || !m_resource->m_surface) {
Debug::log(ERR, "CPopup: orphaned (no surface/resource) and commits??");
onDestroy();
return;
}
if (m_resource->m_surface->m_initialCommit) {
m_resource->m_surface->scheduleConfigure();
return;
}
if (!m_windowOwner.expired() && (!m_windowOwner->m_isMapped || !m_windowOwner->m_workspace->m_visible)) {
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
static auto PLOGDAMAGE = CConfigValue<Hyprlang::INT>("debug:log_damage");
if (*PLOGDAMAGE)
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_windowOwner.lock());
return;
}
if (!m_resource->m_surface->m_mapped)
return;
const auto COORDS = coordsGlobal();
const auto COORDSLOCAL = coordsRelativeToParent();
if (m_lastSize != m_resource->m_surface->m_surface->m_current.size || m_requestedReposition || m_lastPos != COORDSLOCAL) {
CBox box = {localToGlobal(m_lastPos), m_lastSize};
g_pHyprRenderer->damageBox(box);
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
box = {COORDS, m_lastSize};
g_pHyprRenderer->damageBox(box);
m_lastPos = COORDSLOCAL;
}
if (!ignoreSiblings && m_subsurfaceHead)
m_subsurfaceHead->recheckDamageForSubsurfaces();
g_pHyprRenderer->damageSurface(m_wlSurface->resource(), COORDS.x, COORDS.y);
m_requestedReposition = false;
if (!m_layerOwner.expired() && m_layerOwner->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_layerOwner->m_layer));
}
void CPopup::onReposition() {
Debug::log(LOG, "Popup {:x} requests reposition", rc<uintptr_t>(this));
m_requestedReposition = true;
m_lastPos = coordsRelativeToParent();
reposition();
}
void CPopup::reposition() {
const auto COORDS = t1ParentCoords();
const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS);
if (!PMONITOR)
return;
CBox box = {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y};
m_resource->applyPositioning(box, COORDS);
}
SP<Desktop::View::CWLSurface> CPopup::getT1Owner() {
if (m_windowOwner)
return m_windowOwner->wlSurface();
else
return m_layerOwner->wlSurface();
}
Vector2D CPopup::coordsRelativeToParent() {
Vector2D offset;
if (!m_resource)
return m_lastPos;
WP<CPopup> current = m_self;
offset -= current->m_resource->m_surface->m_current.geometry.pos();
while (current->m_parent && current->m_resource) {
offset += current->wlSurface()->resource()->m_current.offset;
offset += current->m_resource->m_geometry.pos();
current = current->m_parent;
}
return offset;
}
Vector2D CPopup::coordsGlobal() {
return localToGlobal(coordsRelativeToParent());
}
Vector2D CPopup::localToGlobal(const Vector2D& rel) {
return t1ParentCoords() + rel;
}
Vector2D CPopup::t1ParentCoords() const {
if (!m_windowOwner.expired())
return m_windowOwner->m_realPosition->value();
if (!m_layerOwner.expired())
return m_layerOwner->m_realPosition->value();
ASSERT(false);
return {};
}
void CPopup::recheckTree() {
WP<CPopup> curr = m_self;
while (curr->m_parent) {
curr = curr->m_parent;
}
curr->recheckChildrenRecursive();
}
void CPopup::recheckChildrenRecursive() {
if (m_inert || !m_wlSurface)
return;
std::vector<WP<CPopup>> cpy;
std::ranges::for_each(m_children, [&cpy](const auto& el) { cpy.emplace_back(el); });
for (auto const& c : cpy) {
c->onCommit(true);
c->recheckChildrenRecursive();
}
}
Vector2D CPopup::size() const {
return m_lastSize;
}
void CPopup::sendScale() {
if (!m_windowOwner.expired())
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_windowOwner->wlSurface()->m_lastScaleFloat);
else if (!m_layerOwner.expired())
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_layerOwner->wlSurface()->m_lastScaleFloat);
else
UNREACHABLE();
}
void CPopup::bfHelper(std::vector<SP<CPopup>> const& nodes, std::function<void(SP<CPopup>, void*)> fn, void* data) {
for (auto const& n : nodes) {
fn(n, data);
}
std::vector<SP<CPopup>> nodes2;
nodes2.reserve(nodes.size() * 2);
for (auto const& n : nodes) {
if (!n)
continue;
for (auto const& c : n->m_children) {
nodes2.emplace_back(c->m_self.lock());
}
}
if (!nodes2.empty())
bfHelper(nodes2, fn, data);
}
void CPopup::breadthfirst(std::function<void(SP<CPopup>, void*)> fn, void* data) {
if (!m_self)
return;
std::vector<SP<CPopup>> popups;
popups.emplace_back(m_self.lock());
bfHelper(popups, fn, data);
}
SP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
std::vector<SP<CPopup>> popups;
breadthfirst([&popups](SP<CPopup> popup, void* data) { popups.push_back(popup); }, &popups);
for (auto const& p : popups | std::views::reverse) {
if (!p->m_resource || !p->m_mapped)
continue;
if (!allowsInput) {
const bool HASSURFACE = p->m_resource && p->m_resource->m_surface;
Vector2D offset = HASSURFACE ? p->m_resource->m_surface->m_current.geometry.pos() : Vector2D{};
Vector2D size = HASSURFACE ? p->m_resource->m_surface->m_current.geometry.size() : p->size();
if (size == Vector2D{})
size = p->size();
const auto BOX = CBox{p->coordsGlobal() + offset, size};
if (BOX.containsPoint(globalCoords))
return p;
} else {
const auto REGION = CRegion{p->wlSurface()->resource()->m_current.input}.intersect(CBox{{}, p->wlSurface()->resource()->m_current.size}).translate(p->coordsGlobal());
if (REGION.containsPoint(globalCoords))
return p;
}
}
return {};
}
bool CPopup::inert() const {
return m_inert;
}
PHLMONITOR CPopup::getMonitor() {
if (!m_windowOwner.expired())
return m_windowOwner->m_monitor.lock();
if (!m_layerOwner.expired())
return m_layerOwner->m_monitor.lock();
return nullptr;
}

106
src/desktop/view/Popup.hpp Normal file
View file

@ -0,0 +1,106 @@
#pragma once
#include <vector>
#include "Subsurface.hpp"
#include "View.hpp"
#include "../../helpers/signal/Signal.hpp"
#include "../../helpers/memory/Memory.hpp"
#include "../../helpers/AnimatedVariable.hpp"
class CXDGPopupResource;
namespace Desktop::View {
class CPopup : public IView {
public:
// dummy head nodes
static SP<CPopup> create(PHLWINDOW pOwner);
static SP<CPopup> create(PHLLS pOwner);
// real nodes
static SP<CPopup> create(SP<CXDGPopupResource> popup, WP<CPopup> pOwner);
static SP<CPopup> fromView(SP<IView>);
virtual ~CPopup();
virtual eViewType type() const;
virtual bool visible() const;
virtual std::optional<CBox> logicalBox() const;
virtual bool desktopComponent() const;
virtual std::optional<CBox> surfaceLogicalBox() const;
SP<Desktop::View::CWLSurface> getT1Owner();
Vector2D coordsRelativeToParent();
Vector2D coordsGlobal();
PHLMONITOR getMonitor();
Vector2D size() const;
void onNewPopup(SP<CXDGPopupResource> popup);
void onDestroy();
void onMap();
void onUnmap();
void onCommit(bool ignoreSiblings = false);
void onReposition();
void recheckTree();
bool inert() const;
// will also loop over this node
void breadthfirst(std::function<void(SP<Desktop::View::CPopup>, void*)> fn, void* data);
SP<Desktop::View::CPopup> at(const Vector2D& globalCoords, bool allowsInput = false);
//
WP<Desktop::View::CPopup> m_self;
bool m_mapped = false;
// fade in-out
PHLANIMVAR<float> m_alpha;
bool m_fadingOut = false;
private:
CPopup();
// T1 owners, each popup has to have one of these
PHLWINDOWREF m_windowOwner;
PHLLSREF m_layerOwner;
// T2 owners
WP<Desktop::View::CPopup> m_parent;
WP<CXDGPopupResource> m_resource;
Vector2D m_lastSize = {};
Vector2D m_lastPos = {};
bool m_requestedReposition = false;
bool m_inert = false;
//
std::vector<SP<Desktop::View::CPopup>> m_children;
SP<Desktop::View::CSubsurface> m_subsurfaceHead;
struct {
CHyprSignalListener newPopup;
CHyprSignalListener destroy;
CHyprSignalListener map;
CHyprSignalListener unmap;
CHyprSignalListener commit;
CHyprSignalListener dismissed;
CHyprSignalListener reposition;
} m_listeners;
void initAllSignals();
void reposition();
void recheckChildrenRecursive();
void sendScale();
void fullyDestroy();
Vector2D localToGlobal(const Vector2D& rel);
Vector2D t1ParentCoords() const;
static void bfHelper(std::vector<SP<CPopup>> const& nodes, std::function<void(SP<CPopup>, void*)> fn, void* data);
};
}

View file

@ -0,0 +1,74 @@
#include "SessionLock.hpp"
#include "../../protocols/SessionLock.hpp"
#include "../../protocols/core/Compositor.hpp"
#include "../../helpers/Monitor.hpp"
#include "../../Compositor.hpp"
using namespace Desktop;
using namespace Desktop::View;
SP<View::CSessionLock> View::CSessionLock::create(SP<CSessionLockSurface> resource) {
auto lock = SP<CSessionLock>(new CSessionLock());
lock->m_surface = resource;
lock->m_self = lock;
lock->init();
return lock;
}
View::CSessionLock::CSessionLock() : IView(CWLSurface::create()) {
;
}
View::CSessionLock::~CSessionLock() {
m_wlSurface->unassign();
}
void View::CSessionLock::init() {
m_listeners.destroy = m_surface->m_events.destroy.listen([this] { std::erase_if(g_pCompositor->m_otherViews, [this](const auto& e) { return e == m_self; }); });
m_wlSurface->assign(m_surface->surface(), m_self.lock());
}
SP<View::CSessionLock> View::CSessionLock::fromView(SP<IView> v) {
if (!v || v->type() != VIEW_TYPE_LOCK_SCREEN)
return nullptr;
return dynamicPointerCast<View::CSessionLock>(v);
}
eViewType View::CSessionLock::type() const {
return VIEW_TYPE_LOCK_SCREEN;
}
bool View::CSessionLock::visible() const {
return m_wlSurface && m_wlSurface->resource() && m_wlSurface->resource()->m_mapped;
}
std::optional<CBox> View::CSessionLock::logicalBox() const {
return surfaceLogicalBox();
}
std::optional<CBox> View::CSessionLock::surfaceLogicalBox() const {
if (!visible())
return std::nullopt;
const auto MON = m_surface->monitor();
if (!MON)
return std::nullopt;
return MON->logicalBox();
}
bool View::CSessionLock::desktopComponent() const {
return true;
}
PHLMONITOR View::CSessionLock::monitor() const {
if (m_surface)
return m_surface->monitor();
return nullptr;
}

View file

@ -0,0 +1,40 @@
#pragma once
#include "../../defines.hpp"
#include <vector>
#include "WLSurface.hpp"
#include "View.hpp"
class CSessionLockSurface;
namespace Desktop::View {
class CSessionLock : public IView {
public:
static SP<CSessionLock> create(SP<CSessionLockSurface> resource);
static SP<CSessionLock> fromView(SP<IView>);
virtual ~CSessionLock();
virtual eViewType type() const;
virtual bool visible() const;
virtual std::optional<CBox> logicalBox() const;
virtual bool desktopComponent() const;
virtual std::optional<CBox> surfaceLogicalBox() const;
PHLMONITOR monitor() const;
WP<CSessionLock> m_self;
private:
CSessionLock();
void init();
struct {
CHyprSignalListener destroy;
} m_listeners;
WP<CSessionLockSurface> m_surface;
};
}

View file

@ -0,0 +1,262 @@
#include "Subsurface.hpp"
#include "../state/FocusState.hpp"
#include "Window.hpp"
#include "../../config/ConfigValue.hpp"
#include "../../protocols/core/Compositor.hpp"
#include "../../protocols/core/Subcompositor.hpp"
#include "../../render/Renderer.hpp"
#include "../../managers/input/InputManager.hpp"
using namespace Desktop;
using namespace Desktop::View;
SP<CSubsurface> CSubsurface::create(PHLWINDOW pOwner) {
auto subsurface = SP<CSubsurface>(new CSubsurface());
subsurface->m_windowParent = pOwner;
subsurface->m_self = subsurface;
subsurface->initSignals();
subsurface->initExistingSubsurfaces(pOwner->wlSurface()->resource());
return subsurface;
}
SP<CSubsurface> CSubsurface::create(WP<Desktop::View::CPopup> pOwner) {
auto subsurface = SP<CSubsurface>(new CSubsurface());
subsurface->m_popupParent = pOwner;
subsurface->m_self = subsurface;
subsurface->initSignals();
subsurface->initExistingSubsurfaces(pOwner->wlSurface()->resource());
return subsurface;
}
SP<CSubsurface> CSubsurface::create(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner) {
auto subsurface = SP<CSubsurface>(new CSubsurface());
subsurface->m_windowParent = pOwner;
subsurface->m_subsurface = pSubsurface;
subsurface->m_self = subsurface;
subsurface->wlSurface() = CWLSurface::create();
subsurface->wlSurface()->assign(pSubsurface->m_surface.lock(), subsurface);
subsurface->initSignals();
subsurface->initExistingSubsurfaces(pSubsurface->m_surface.lock());
return subsurface;
}
SP<CSubsurface> CSubsurface::create(SP<CWLSubsurfaceResource> pSubsurface, WP<Desktop::View::CPopup> pOwner) {
auto subsurface = SP<CSubsurface>(new CSubsurface());
subsurface->m_popupParent = pOwner;
subsurface->m_subsurface = pSubsurface;
subsurface->m_self = subsurface;
subsurface->wlSurface() = CWLSurface::create();
subsurface->wlSurface()->assign(pSubsurface->m_surface.lock(), subsurface);
subsurface->initSignals();
subsurface->initExistingSubsurfaces(pSubsurface->m_surface.lock());
return subsurface;
}
SP<CSubsurface> CSubsurface::fromView(SP<IView> v) {
if (!v || v->type() != VIEW_TYPE_SUBSURFACE)
return nullptr;
return dynamicPointerCast<CSubsurface>(v);
}
CSubsurface::CSubsurface() : IView(CWLSurface::create()) {
;
}
eViewType CSubsurface::type() const {
return VIEW_TYPE_SUBSURFACE;
}
bool CSubsurface::visible() const {
if (!m_wlSurface || !m_wlSurface->resource() || !m_wlSurface->resource()->m_mapped)
return false;
if (!m_windowParent.expired())
return g_pHyprRenderer->shouldRenderWindow(m_windowParent.lock());
if (m_popupParent)
return m_popupParent->visible();
if (m_parent)
return m_parent->visible();
return false;
}
bool CSubsurface::desktopComponent() const {
return true;
}
std::optional<CBox> CSubsurface::logicalBox() const {
return surfaceLogicalBox();
}
std::optional<CBox> CSubsurface::surfaceLogicalBox() const {
if (!visible())
return std::nullopt;
return CBox{coordsGlobal(), m_lastSize};
}
void CSubsurface::initSignals() {
if (m_subsurface) {
m_listeners.commitSubsurface = m_subsurface->m_surface->m_events.commit.listen([this] { onCommit(); });
m_listeners.destroySubsurface = m_subsurface->m_events.destroy.listen([this] { onDestroy(); });
m_listeners.mapSubsurface = m_subsurface->m_surface->m_events.map.listen([this] { onMap(); });
m_listeners.unmapSubsurface = m_subsurface->m_surface->m_events.unmap.listen([this] { onUnmap(); });
m_listeners.newSubsurface = m_subsurface->m_surface->m_events.newSubsurface.listen([this](const auto& resource) { onNewSubsurface(resource); });
} else {
if (m_windowParent)
m_listeners.newSubsurface = m_windowParent->wlSurface()->resource()->m_events.newSubsurface.listen([this](const auto& resource) { onNewSubsurface(resource); });
else if (m_popupParent)
m_listeners.newSubsurface = m_popupParent->wlSurface()->resource()->m_events.newSubsurface.listen([this](const auto& resource) { onNewSubsurface(resource); });
else
ASSERT(false);
}
}
void CSubsurface::checkSiblingDamage() {
if (!m_parent)
return; // ??????????
const double SCALE = m_windowParent.lock() && m_windowParent->m_isX11 ? 1.0 / m_windowParent->m_X11SurfaceScaledBy : 1.0;
for (auto const& n : m_parent->m_children) {
if (n.get() == this)
continue;
const auto COORDS = n->coordsGlobal();
g_pHyprRenderer->damageSurface(n->wlSurface()->resource(), COORDS.x, COORDS.y, SCALE);
}
}
void CSubsurface::recheckDamageForSubsurfaces() {
for (auto const& n : m_children) {
const auto COORDS = n->coordsGlobal();
g_pHyprRenderer->damageSurface(n->wlSurface()->resource(), COORDS.x, COORDS.y);
}
}
void CSubsurface::onCommit() {
// no damaging if it's not visible
if (!m_windowParent.expired() && (!m_windowParent->m_isMapped || !m_windowParent->m_workspace->m_visible)) {
m_lastSize = m_wlSurface->resource()->m_current.size;
static auto PLOGDAMAGE = CConfigValue<Hyprlang::INT>("debug:log_damage");
if (*PLOGDAMAGE)
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_windowParent.lock());
return;
}
const auto COORDS = coordsGlobal();
g_pHyprRenderer->damageSurface(m_wlSurface->resource(), COORDS.x, COORDS.y);
if (m_popupParent && !m_popupParent->inert() && m_popupParent->wlSurface())
m_popupParent->recheckTree();
if (!m_windowParent.expired()) // I hate you firefox why are you doing this
m_windowParent->m_popupHead->recheckTree();
// I do not think this is correct, but it solves a lot of issues with some apps (e.g. firefox)
checkSiblingDamage();
if (m_lastSize != m_wlSurface->resource()->m_current.size || m_lastPosition != m_subsurface->m_position) {
damageLastArea();
m_lastSize = m_wlSurface->resource()->m_current.size;
m_lastPosition = m_subsurface->m_position;
}
}
void CSubsurface::onDestroy() {
// destroy children
m_children.clear();
m_inert = true;
if (!m_subsurface)
return; // dummy node, nothing to do, it's the parent dying
// kill ourselves
std::erase_if(m_parent->m_children, [this](const auto& other) { return other.get() == this; });
}
void CSubsurface::onNewSubsurface(SP<CWLSubsurfaceResource> pSubsurface) {
WP<CSubsurface> PSUBSURFACE;
if (!m_windowParent.expired())
PSUBSURFACE = m_children.emplace_back(CSubsurface::create(pSubsurface, m_windowParent.lock()));
else if (m_popupParent)
PSUBSURFACE = m_children.emplace_back(CSubsurface::create(pSubsurface, m_popupParent));
PSUBSURFACE->m_self = PSUBSURFACE;
ASSERT(PSUBSURFACE);
PSUBSURFACE->m_parent = m_self;
}
void CSubsurface::onMap() {
m_lastSize = m_wlSurface->resource()->m_current.size;
m_lastPosition = m_subsurface->m_position;
const auto COORDS = coordsGlobal();
CBox box{COORDS, m_lastSize};
box.expand(4);
g_pHyprRenderer->damageBox(box);
if (!m_windowParent.expired())
m_windowParent->updateSurfaceScaleTransformDetails();
}
void CSubsurface::onUnmap() {
damageLastArea();
if (m_wlSurface->resource() == Desktop::focusState()->surface())
g_pInputManager->releaseAllMouseButtons();
g_pInputManager->simulateMouseMovement();
// TODO: should this remove children? Currently it won't, only on .destroy
}
void CSubsurface::damageLastArea() {
const auto COORDS = coordsGlobal() + m_lastPosition - m_subsurface->m_position;
const Vector2D MAX_DAMAGE_SIZE = m_wlSurface && m_wlSurface->resource() ?
Vector2D{
std::max(m_lastSize.x, m_wlSurface->resource()->m_current.size.x),
std::max(m_lastSize.y, m_wlSurface->resource()->m_current.size.y),
} :
m_lastSize;
CBox box{COORDS, m_lastSize};
box.expand(4);
g_pHyprRenderer->damageBox(box);
}
Vector2D CSubsurface::coordsRelativeToParent() const {
if (!m_subsurface)
return {};
return m_subsurface->posRelativeToParent();
}
Vector2D CSubsurface::coordsGlobal() const {
Vector2D coords = coordsRelativeToParent();
if (!m_windowParent.expired())
coords += m_windowParent->m_realPosition->value();
else if (m_popupParent)
coords += m_popupParent->coordsGlobal();
return coords;
}
void CSubsurface::initExistingSubsurfaces(SP<CWLSurfaceResource> pSurface) {
for (auto const& s : pSurface->m_subsurfaces) {
if (!s || s->m_surface->m_hlSurface /* already assigned */)
continue;
onNewSubsurface(s.lock());
}
}
Vector2D CSubsurface::size() {
return m_wlSurface->resource()->m_current.size;
}

View file

@ -0,0 +1,77 @@
#pragma once
#include "../../defines.hpp"
#include <vector>
#include "WLSurface.hpp"
#include "View.hpp"
class CWLSubsurfaceResource;
namespace Desktop::View {
class CPopup;
class CSubsurface : public IView {
public:
// root dummy nodes
static SP<CSubsurface> create(PHLWINDOW pOwner);
static SP<CSubsurface> create(WP<Desktop::View::CPopup> pOwner);
// real nodes
static SP<CSubsurface> create(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner);
static SP<CSubsurface> create(SP<CWLSubsurfaceResource> pSubsurface, WP<Desktop::View::CPopup> pOwner);
static SP<CSubsurface> fromView(SP<IView>);
virtual ~CSubsurface() = default;
virtual eViewType type() const;
virtual bool visible() const;
virtual std::optional<CBox> logicalBox() const;
virtual bool desktopComponent() const;
virtual std::optional<CBox> surfaceLogicalBox() const;
Vector2D coordsRelativeToParent() const;
Vector2D coordsGlobal() const;
Vector2D size();
void onCommit();
void onDestroy();
void onNewSubsurface(SP<CWLSubsurfaceResource> pSubsurface);
void onMap();
void onUnmap();
void recheckDamageForSubsurfaces();
WP<Desktop::View::CSubsurface> m_self;
private:
CSubsurface();
struct {
CHyprSignalListener destroySubsurface;
CHyprSignalListener commitSubsurface;
CHyprSignalListener mapSubsurface;
CHyprSignalListener unmapSubsurface;
CHyprSignalListener newSubsurface;
} m_listeners;
WP<CWLSubsurfaceResource> m_subsurface;
Vector2D m_lastSize = {};
Vector2D m_lastPosition = {};
// if nullptr, means it's a dummy node
WP<Desktop::View::CSubsurface> m_parent;
PHLWINDOWREF m_windowParent;
WP<Desktop::View::CPopup> m_popupParent;
std::vector<SP<Desktop::View::CSubsurface>> m_children;
bool m_inert = false;
void initSignals();
void initExistingSubsurfaces(SP<CWLSurfaceResource> pSurface);
void checkSiblingDamage();
void damageLastArea();
};
}

16
src/desktop/view/View.cpp Normal file
View file

@ -0,0 +1,16 @@
#include "View.hpp"
using namespace Desktop;
using namespace Desktop::View;
SP<Desktop::View::CWLSurface> IView::wlSurface() const {
return m_wlSurface;
}
IView::IView(SP<Desktop::View::CWLSurface> pWlSurface) : m_wlSurface(pWlSurface) {
;
}
SP<CWLSurfaceResource> IView::resource() const {
return m_wlSurface ? m_wlSurface->resource() : nullptr;
}

32
src/desktop/view/View.hpp Normal file
View file

@ -0,0 +1,32 @@
#pragma once
#include "WLSurface.hpp"
#include "../../helpers/math/Math.hpp"
namespace Desktop::View {
enum eViewType : uint8_t {
VIEW_TYPE_WINDOW = 0,
VIEW_TYPE_SUBSURFACE,
VIEW_TYPE_POPUP,
VIEW_TYPE_LAYER_SURFACE,
VIEW_TYPE_LOCK_SCREEN,
};
class IView {
public:
virtual ~IView() = default;
virtual SP<Desktop::View::CWLSurface> wlSurface() const;
virtual SP<CWLSurfaceResource> resource() const;
virtual eViewType type() const = 0;
virtual bool visible() const = 0;
virtual bool desktopComponent() const = 0;
virtual std::optional<CBox> logicalBox() const = 0;
virtual std::optional<CBox> surfaceLogicalBox() const = 0;
protected:
IView(SP<Desktop::View::CWLSurface> pWlSurface);
SP<Desktop::View::CWLSurface> m_wlSurface;
};
};

View file

@ -0,0 +1,194 @@
#include "WLSurface.hpp"
#include "LayerSurface.hpp"
#include "Window.hpp"
#include "../../protocols/core/Compositor.hpp"
#include "../../protocols/LayerShell.hpp"
#include "../../render/Renderer.hpp"
using namespace Desktop;
using namespace Desktop::View;
void CWLSurface::assign(SP<CWLSurfaceResource> pSurface) {
m_resource = pSurface;
init();
m_inert = false;
}
void CWLSurface::assign(SP<CWLSurfaceResource> pSurface, SP<IView> pOwner) {
m_view = pOwner;
m_resource = pSurface;
init();
m_inert = false;
}
void CWLSurface::unassign() {
destroy();
}
CWLSurface::~CWLSurface() {
destroy();
}
bool CWLSurface::exists() const {
return m_resource;
}
SP<CWLSurfaceResource> CWLSurface::resource() const {
return m_resource.lock();
}
bool CWLSurface::small() const {
if (!m_view || !m_view->visible() || m_view->type() != VIEW_TYPE_WINDOW || !exists())
return false;
if (!m_resource->m_current.texture)
return false;
const auto O = dynamicPointerCast<CWindow>(m_view.lock());
const auto REPORTED_SIZE = O->getReportedSize();
return REPORTED_SIZE.x > m_resource->m_current.size.x + 1 || REPORTED_SIZE.y > m_resource->m_current.size.y + 1;
}
Vector2D CWLSurface::correctSmallVec() const {
if (!m_view || !m_view->visible() || m_view->type() != VIEW_TYPE_WINDOW || !exists() || !small() || !m_fillIgnoreSmall)
return {};
const auto SIZE = getViewporterCorrectedSize();
const auto O = dynamicPointerCast<CWindow>(m_view.lock());
const auto REP = O->getReportedSize();
return Vector2D{(REP.x - SIZE.x) / 2, (REP.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * (O->m_realSize->value() / REP);
}
Vector2D CWLSurface::correctSmallVecBuf() const {
if (!exists() || !small() || m_fillIgnoreSmall || !m_resource->m_current.texture)
return {};
const auto SIZE = getViewporterCorrectedSize();
const auto BS = m_resource->m_current.bufferSize;
return Vector2D{(BS.x - SIZE.x) / 2, (BS.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY});
}
Vector2D CWLSurface::getViewporterCorrectedSize() const {
if (!exists() || !m_resource->m_current.texture)
return {};
return m_resource->m_current.viewport.hasDestination ? m_resource->m_current.viewport.destination : m_resource->m_current.bufferSize;
}
CRegion CWLSurface::computeDamage() const {
if (!m_resource->m_current.texture)
return {};
CRegion damage = m_resource->m_current.accumulateBufferDamage();
damage.transform(wlTransformToHyprutils(m_resource->m_current.transform), m_resource->m_current.bufferSize.x, m_resource->m_current.bufferSize.y);
const auto BUFSIZE = m_resource->m_current.bufferSize;
const auto CORRECTVEC = correctSmallVecBuf();
if (m_resource->m_current.viewport.hasSource)
damage.intersect(m_resource->m_current.viewport.source);
const auto SCALEDSRCSIZE =
m_resource->m_current.viewport.hasSource ? m_resource->m_current.viewport.source.size() * m_resource->m_current.scale : m_resource->m_current.bufferSize;
damage.scale({BUFSIZE.x / SCALEDSRCSIZE.x, BUFSIZE.y / SCALEDSRCSIZE.y});
damage.translate(CORRECTVEC);
// go from buffer coords in the damage to hl logical
const auto BOX = getSurfaceBoxGlobal();
const auto SURFSIZE = m_resource->m_current.size;
const Vector2D SCALE = SURFSIZE / m_resource->m_current.bufferSize;
damage.scale(SCALE);
if (BOX.has_value()) {
if (m_view->type() == VIEW_TYPE_WINDOW)
damage.intersect(CBox{{}, BOX->size() * dynamicPointerCast<CWindow>(m_view.lock())->m_X11SurfaceScaledBy});
else
damage.intersect(CBox{{}, BOX->size()});
}
return damage;
}
void CWLSurface::destroy() {
if (!m_resource)
return;
m_events.destroy.emit();
m_constraint.reset();
m_listeners.destroy.reset();
m_resource->m_hlSurface.reset();
m_view.reset();
m_inert = true;
if (g_pHyprRenderer && g_pHyprRenderer->m_lastCursorData.surf && g_pHyprRenderer->m_lastCursorData.surf->get() == this)
g_pHyprRenderer->m_lastCursorData.surf.reset();
m_resource.reset();
Debug::log(LOG, "CWLSurface {:x} called destroy()", rc<uintptr_t>(this));
}
void CWLSurface::init() {
if (!m_resource)
return;
RASSERT(!m_resource->m_hlSurface, "Attempted to duplicate CWLSurface ownership!");
m_resource->m_hlSurface = m_self.lock();
m_listeners.destroy = m_resource->m_events.destroy.listen([this] { destroy(); });
Debug::log(LOG, "CWLSurface {:x} called init()", rc<uintptr_t>(this));
}
SP<IView> CWLSurface::view() const {
return m_view.lock();
}
bool CWLSurface::desktopComponent() const {
return m_view && m_view->visible();
}
std::optional<CBox> CWLSurface::getSurfaceBoxGlobal() const {
if (!desktopComponent())
return {};
return m_view->surfaceLogicalBox();
}
void CWLSurface::appendConstraint(WP<CPointerConstraint> constraint) {
m_constraint = constraint;
}
SP<CPointerConstraint> CWLSurface::constraint() const {
return m_constraint.lock();
}
bool CWLSurface::visible() {
if (m_view)
return m_view->visible();
return true; // non-desktop, we don't know much.
}
SP<Desktop::View::CWLSurface> CWLSurface::fromResource(SP<CWLSurfaceResource> pSurface) {
if (!pSurface)
return nullptr;
return pSurface->m_hlSurface.lock();
}
bool CWLSurface::keyboardFocusable() const {
if (!m_view)
return false;
if (m_view->type() == VIEW_TYPE_WINDOW || m_view->type() == VIEW_TYPE_SUBSURFACE || m_view->type() == VIEW_TYPE_POPUP)
return true;
if (const auto LS = CLayerSurface::fromView(m_view.lock()); LS && LS->m_layerSurface)
return LS->m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
return false;
}

View file

@ -0,0 +1,118 @@
#pragma once
#include "../../defines.hpp"
#include "../../helpers/math/Math.hpp"
#include "../../helpers/signal/Signal.hpp"
class CPointerConstraint;
class CWLSurfaceResource;
namespace Desktop::View {
class CSubsurface;
class CPopup;
class IView;
class CWLSurface {
public:
static SP<Desktop::View::CWLSurface> create() {
auto p = SP<Desktop::View::CWLSurface>(new CWLSurface);
p->m_self = p;
return p;
}
~CWLSurface();
// anonymous surfaces are non-desktop components, e.g. a cursor surface or a DnD
void assign(SP<CWLSurfaceResource> pSurface);
void assign(SP<CWLSurfaceResource> pSurface, SP<IView> pOwner);
void unassign();
CWLSurface(const CWLSurface&) = delete;
CWLSurface(CWLSurface&&) = delete;
CWLSurface& operator=(const CWLSurface&) = delete;
CWLSurface& operator=(CWLSurface&&) = delete;
SP<CWLSurfaceResource> resource() const;
bool exists() const;
bool small() const; // means surface is smaller than the requested size
Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces
Vector2D correctSmallVecBuf() const; // returns a corrective vector for small() surfaces, in BL coords
Vector2D getViewporterCorrectedSize() const;
CRegion computeDamage() const; // logical coordinates. May be wrong if the surface is unassigned
bool visible();
bool keyboardFocusable() const;
SP<IView> view() const;
// desktop components misc utils
std::optional<CBox> getSurfaceBoxGlobal() const;
void appendConstraint(WP<CPointerConstraint> constraint);
SP<CPointerConstraint> constraint() const;
// allow stretching. Useful for plugins.
bool m_fillIgnoreSmall = false;
// track surface data and avoid dupes
float m_lastScaleFloat = 0;
int m_lastScaleInt = 0;
wl_output_transform m_lastTransform = sc<wl_output_transform>(-1);
//
CWLSurface& operator=(SP<CWLSurfaceResource> pSurface) {
destroy();
m_resource = pSurface;
init();
return *this;
}
bool operator==(const CWLSurface& other) const {
return other.resource() == resource();
}
bool operator==(const SP<CWLSurfaceResource> other) const {
return other == resource();
}
explicit operator bool() const {
return exists();
}
static SP<Desktop::View::CWLSurface> fromResource(SP<CWLSurfaceResource> pSurface);
// used by the alpha-modifier protocol
float m_alphaModifier = 1.F;
// used by the hyprland-surface protocol
float m_overallOpacity = 1.F;
CRegion m_visibleRegion;
struct {
CSignalT<> destroy;
} m_events;
WP<Desktop::View::CWLSurface> m_self;
private:
CWLSurface() = default;
bool m_inert = true;
WP<CWLSurfaceResource> m_resource;
WP<IView> m_view;
//
WP<CPointerConstraint> m_constraint;
void destroy();
void init();
bool desktopComponent() const;
struct {
CHyprSignalListener destroy;
} m_listeners;
friend class ::CPointerConstraint;
friend class CXxColorManagerV4;
};
}

2703
src/desktop/view/Window.cpp Normal file

File diff suppressed because it is too large Load diff

454
src/desktop/view/Window.hpp Normal file
View file

@ -0,0 +1,454 @@
#pragma once
#include <vector>
#include <string>
#include <optional>
#include "View.hpp"
#include "../../config/ConfigDataValues.hpp"
#include "../../helpers/AnimatedVariable.hpp"
#include "../../helpers/TagKeeper.hpp"
#include "../../macros.hpp"
#include "../../managers/XWaylandManager.hpp"
#include "../../render/decorations/IHyprWindowDecoration.hpp"
#include "../../render/Transformer.hpp"
#include "../DesktopTypes.hpp"
#include "Popup.hpp"
#include "Subsurface.hpp"
#include "WLSurface.hpp"
#include "../Workspace.hpp"
#include "../rule/windowRule/WindowRuleApplicator.hpp"
#include "../../protocols/types/ContentType.hpp"
class CXDGSurfaceResource;
class CXWaylandSurface;
struct SWorkspaceRule;
class IWindowTransformer;
namespace Desktop::View {
enum eGroupRules : uint8_t {
// effective only during first map, except for _ALWAYS variant
GROUP_NONE = 0,
GROUP_SET = 1 << 0, // Open as new group or add to focused group
GROUP_SET_ALWAYS = 1 << 1,
GROUP_BARRED = 1 << 2, // Don't insert to focused group.
GROUP_LOCK = 1 << 3, // Lock m_sGroupData.lock
GROUP_LOCK_ALWAYS = 1 << 4,
GROUP_INVADE = 1 << 5, // Force enter a group, event if lock is engaged
GROUP_OVERRIDE = 1 << 6, // Override other rules
};
enum eGetWindowProperties : uint8_t {
WINDOW_ONLY = 0,
RESERVED_EXTENTS = 1 << 0,
INPUT_EXTENTS = 1 << 1,
FULL_EXTENTS = 1 << 2,
FLOATING_ONLY = 1 << 3,
ALLOW_FLOATING = 1 << 4,
USE_PROP_TILED = 1 << 5,
SKIP_FULLSCREEN_PRIORITY = 1 << 6,
FOCUS_PRIORITY = 1 << 7,
};
enum eSuppressEvents : uint8_t {
SUPPRESS_NONE = 0,
SUPPRESS_FULLSCREEN = 1 << 0,
SUPPRESS_MAXIMIZE = 1 << 1,
SUPPRESS_ACTIVATE = 1 << 2,
SUPPRESS_ACTIVATE_FOCUSONLY = 1 << 3,
SUPPRESS_FULLSCREEN_OUTPUT = 1 << 4,
};
struct SInitialWorkspaceToken {
PHLWINDOWREF primaryOwner;
std::string workspace;
};
struct SFullscreenState {
eFullscreenMode internal = FSMODE_NONE;
eFullscreenMode client = FSMODE_NONE;
};
class CWindow : public IView {
public:
static PHLWINDOW create(SP<CXDGSurfaceResource>);
static PHLWINDOW create(SP<CXWaylandSurface>);
static PHLWINDOW fromView(SP<IView>);
private:
CWindow(SP<CXDGSurfaceResource> resource);
CWindow(SP<CXWaylandSurface> surface);
public:
virtual ~CWindow();
virtual eViewType type() const;
virtual bool visible() const;
virtual std::optional<CBox> logicalBox() const;
virtual bool desktopComponent() const;
virtual std::optional<CBox> surfaceLogicalBox() const;
struct {
CSignalT<> destroy;
} m_events;
WP<CXDGSurfaceResource> m_xdgSurface;
WP<CXWaylandSurface> m_xwaylandSurface;
// this is the position and size of the "bounding box"
Vector2D m_position = Vector2D(0, 0);
Vector2D m_size = Vector2D(0, 0);
// this is the real position and size used to draw the thing
PHLANIMVAR<Vector2D> m_realPosition;
PHLANIMVAR<Vector2D> m_realSize;
// for not spamming the protocols
Vector2D m_reportedPosition;
Vector2D m_reportedSize;
Vector2D m_pendingReportedSize;
std::optional<std::pair<uint32_t, Vector2D>> m_pendingSizeAck;
std::vector<std::pair<uint32_t, Vector2D>> m_pendingSizeAcks;
// for restoring floating statuses
Vector2D m_lastFloatingSize;
Vector2D m_lastFloatingPosition;
// for floating window offset in workspace animations
Vector2D m_floatingOffset = Vector2D(0, 0);
// this is used for pseudotiling
bool m_isPseudotiled = false;
Vector2D m_pseudoSize = Vector2D(1280, 720);
// for recovering relative cursor position
Vector2D m_relativeCursorCoordsOnLastWarp = Vector2D(-1, -1);
bool m_firstMap = false; // for layouts
bool m_isFloating = false;
bool m_draggingTiled = false; // for dragging around tiled windows
SFullscreenState m_fullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE};
std::string m_title = "";
std::string m_class = "";
std::string m_initialTitle = "";
std::string m_initialClass = "";
PHLWORKSPACE m_workspace;
PHLMONITORREF m_monitor;
bool m_isMapped = false;
bool m_requestsFloat = false;
// This is for fullscreen apps
bool m_createdOverFullscreen = false;
// XWayland stuff
bool m_isX11 = false;
bool m_X11DoesntWantBorders = false;
bool m_X11ShouldntFocus = false;
float m_X11SurfaceScaledBy = 1.f;
//
// For nofocus
bool m_noInitialFocus = false;
// Fullscreen and Maximize
bool m_wantsInitialFullscreen = false;
MONITORID m_wantsInitialFullscreenMonitor = MONITOR_INVALID;
// bitfield suppressEvents
uint64_t m_suppressedEvents = SUPPRESS_NONE;
// desktop components
SP<Desktop::View::CSubsurface> m_subsurfaceHead;
SP<Desktop::View::CPopup> m_popupHead;
// Animated border
CGradientValueData m_realBorderColor = {0};
CGradientValueData m_realBorderColorPrevious = {0};
PHLANIMVAR<float> m_borderFadeAnimationProgress;
PHLANIMVAR<float> m_borderAngleAnimationProgress;
// Fade in-out
PHLANIMVAR<float> m_alpha;
bool m_fadingOut = false;
bool m_readyToDelete = false;
Vector2D m_originalClosedPos; // these will be used for calculations later on in
Vector2D m_originalClosedSize; // drawing the closing animations
SBoxExtents m_originalClosedExtents;
bool m_animatingIn = false;
// For pinned (sticky) windows
bool m_pinned = false;
// For preserving pinned state when fullscreening a pinned window
bool m_pinFullscreened = false;
// urgency hint
bool m_isUrgent = false;
// for proper cycling. While cycling we can't just move the pointers, so we need to keep track of the last cycled window.
PHLWINDOWREF m_lastCycledWindow;
// Window decorations
// TODO: make this a SP.
std::vector<UP<IHyprWindowDecoration>> m_windowDecorations;
std::vector<IHyprWindowDecoration*> m_decosToRemove;
// Special render data, rules, etc
UP<Desktop::Rule::CWindowRuleApplicator> m_ruleApplicator;
// Transformers
std::vector<UP<IWindowTransformer>> m_transformers;
// for alpha
PHLANIMVAR<float> m_activeInactiveAlpha;
PHLANIMVAR<float> m_movingFromWorkspaceAlpha;
// animated shadow color
PHLANIMVAR<CHyprColor> m_realShadowColor;
// animated tint
PHLANIMVAR<float> m_dimPercent;
// animate moving to an invisible workspace
int m_monitorMovedFrom = -1; // -1 means not moving
PHLANIMVAR<float> m_movingToWorkspaceAlpha;
// swallowing
PHLWINDOWREF m_swallowed;
bool m_currentlySwallowed = false;
bool m_groupSwallowed = false;
// for toplevel monitor events
MONITORID m_lastSurfaceMonitorID = -1;
// initial token. Will be unregistered on workspace change or timeout of 2 minutes
std::string m_initialWorkspaceToken = "";
// for groups
struct SGroupData {
PHLWINDOWREF pNextWindow; // nullptr means no grouping. Self means single group.
bool head = false;
bool locked = false; // per group lock
bool deny = false; // deny window from enter a group or made a group
} m_groupData;
uint16_t m_groupRules = Desktop::View::GROUP_NONE;
bool m_tearingHint = false;
// ANR
PHLANIMVAR<float> m_notRespondingTint;
// For the noclosefor windowrule
Time::steady_tp m_closeableSince = Time::steadyNow();
// For the list lookup
bool operator==(const CWindow& rhs) const {
return m_xdgSurface == rhs.m_xdgSurface && m_xwaylandSurface == rhs.m_xwaylandSurface && m_position == rhs.m_position && m_size == rhs.m_size &&
m_fadingOut == rhs.m_fadingOut;
}
// methods
CBox getFullWindowBoundingBox() const;
SBoxExtents getFullWindowExtents() const;
CBox getWindowBoxUnified(uint64_t props);
SBoxExtents getWindowExtentsUnified(uint64_t props);
CBox getWindowIdealBoundingBoxIgnoreReserved();
void addWindowDeco(UP<IHyprWindowDecoration> deco);
void updateWindowDecos();
void removeWindowDeco(IHyprWindowDecoration* deco);
void uncacheWindowDecos();
bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {});
pid_t getPID();
IHyprWindowDecoration* getDecorationByType(eDecorationType);
void updateToplevel();
void updateSurfaceScaleTransformDetails(bool force = false);
void moveToWorkspace(PHLWORKSPACE);
PHLWINDOW x11TransientFor();
void onUnmap();
void onMap();
void setHidden(bool hidden);
bool isHidden();
void updateDecorationValues();
SBoxExtents getFullWindowReservedArea();
Vector2D middle();
bool opaque();
float rounding();
float roundingPower();
bool canBeTorn();
void setSuspended(bool suspend);
bool visibleOnMonitor(PHLMONITOR pMonitor);
WORKSPACEID workspaceID();
MONITORID monitorID();
bool onSpecialWorkspace();
void activate(bool force = false);
int surfacesCount();
void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
bool isFullscreen();
bool isEffectiveInternalFSMode(const eFullscreenMode) const;
int getRealBorderSize() const;
float getScrollMouse();
float getScrollTouchpad();
bool isScrollMouseOverridden();
bool isScrollTouchpadOverridden();
void updateWindowData();
void updateWindowData(const SWorkspaceRule&);
void onBorderAngleAnimEnd(WP<Hyprutils::Animation::CBaseAnimatedVariable> pav);
bool isInCurvedCorner(double x, double y);
bool hasPopupAt(const Vector2D& pos);
int popupsCount();
void applyGroupRules();
void createGroup();
void destroyGroup();
PHLWINDOW getGroupHead();
PHLWINDOW getGroupTail();
PHLWINDOW getGroupCurrent();
PHLWINDOW getGroupPrevious();
PHLWINDOW getGroupWindowByIndex(int);
bool hasInGroup(PHLWINDOW);
int getGroupSize();
bool canBeGroupedInto(PHLWINDOW pWindow);
void setGroupCurrent(PHLWINDOW pWindow);
void insertWindowToGroup(PHLWINDOW pWindow);
void updateGroupOutputs();
void switchWithWindowInGroup(PHLWINDOW pWindow);
void setAnimationsToMove();
void onWorkspaceAnimUpdate();
void onFocusAnimUpdate();
void onUpdateState();
void onUpdateMeta();
void onX11ConfigureRequest(CBox box);
void onResourceChangeX11();
std::string fetchTitle();
std::string fetchClass();
void warpCursor(bool force = false);
PHLWINDOW getSwallower();
bool isX11OverrideRedirect();
bool isModal();
Vector2D requestedMinSize();
Vector2D requestedMaxSize();
Vector2D realToReportSize();
Vector2D realToReportPosition();
Vector2D xwaylandSizeToReal(Vector2D size);
Vector2D xwaylandPositionToReal(Vector2D size);
void updateX11SurfaceScale();
void sendWindowSize(bool force = false);
NContentType::eContentType getContentType();
void setContentType(NContentType::eContentType contentType);
void deactivateGroupMembers();
bool isNotResponding();
std::optional<std::string> xdgTag();
std::optional<std::string> xdgDescription();
PHLWINDOW parent();
bool priorityFocus();
SP<CWLSurfaceResource> getSolitaryResource();
Vector2D getReportedSize();
std::optional<Vector2D> calculateExpression(const std::string& s);
CBox getWindowMainSurfaceBox() const {
return {m_realPosition->value().x, m_realPosition->value().y, m_realSize->value().x, m_realSize->value().y};
}
// listeners
void onAck(uint32_t serial);
//
std::unordered_map<std::string, std::string> getEnv();
//
PHLWINDOWREF m_self;
// make private once we move listeners to inside CWindow
struct {
CHyprSignalListener map;
CHyprSignalListener ack;
CHyprSignalListener unmap;
CHyprSignalListener commit;
CHyprSignalListener destroy;
CHyprSignalListener activate;
CHyprSignalListener configureRequest;
CHyprSignalListener setGeometry;
CHyprSignalListener updateState;
CHyprSignalListener updateMetadata;
CHyprSignalListener resourceChange;
} m_listeners;
private:
std::optional<double> calculateSingleExpr(const std::string& s);
void mapWindow();
void unmapWindow();
void commitWindow();
void destroyWindow();
void activateX11();
void unmanagedSetGeometry();
// For hidden windows and stuff
bool m_hidden = false;
bool m_suspended = false;
WORKSPACEID m_lastWorkspace = WORKSPACE_INVALID;
};
inline bool valid(PHLWINDOW w) {
return w.get();
}
inline bool valid(PHLWINDOWREF w) {
return !w.expired();
}
inline bool validMapped(PHLWINDOW w) {
if (!valid(w))
return false;
return w->m_isMapped;
}
inline bool validMapped(PHLWINDOWREF w) {
if (!valid(w))
return false;
return w->m_isMapped;
}
}
/**
format specification
- 'x', only address, equivalent of (uintpr_t)CWindow*
- 'm', with monitor id
- 'w', with workspace id
- 'c', with application class
*/
template <typename CharT>
struct std::formatter<PHLWINDOW, CharT> : std::formatter<CharT> {
bool formatAddressOnly = false;
bool formatWorkspace = false;
bool formatMonitor = false;
bool formatClass = false;
FORMAT_PARSE( //
FORMAT_FLAG('x', formatAddressOnly) //
FORMAT_FLAG('m', formatMonitor) //
FORMAT_FLAG('w', formatWorkspace) //
FORMAT_FLAG('c', formatClass),
PHLWINDOW)
template <typename FormatContext>
auto format(PHLWINDOW const& w, FormatContext& ctx) const {
auto&& out = ctx.out();
if (formatAddressOnly)
return std::format_to(out, "{:x}", rc<uintptr_t>(w.get()));
if (!w)
return std::format_to(out, "[Window nullptr]");
std::format_to(out, "[");
std::format_to(out, "Window {:x}: title: \"{}\"", rc<uintptr_t>(w.get()), w->m_title);
if (formatWorkspace)
std::format_to(out, ", workspace: {}", w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID);
if (formatMonitor)
std::format_to(out, ", monitor: {}", w->monitorID());
if (formatClass)
std::format_to(out, ", class: {}", w->m_class);
return std::format_to(out, "]");
}
};