desktop: cleanup, unify desktop elements as views (#12563)
This commit is contained in:
parent
834f019bab
commit
920353370b
105 changed files with 2636 additions and 2337 deletions
82
src/desktop/view/GlobalViewMethods.cpp
Normal file
82
src/desktop/view/GlobalViewMethods.cpp
Normal 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;
|
||||
}
|
||||
11
src/desktop/view/GlobalViewMethods.hpp
Normal file
11
src/desktop/view/GlobalViewMethods.hpp
Normal 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);
|
||||
};
|
||||
462
src/desktop/view/LayerSurface.cpp
Normal file
462
src/desktop/view/LayerSurface.cpp
Normal 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;
|
||||
}
|
||||
105
src/desktop/view/LayerSurface.hpp
Normal file
105
src/desktop/view/LayerSurface.hpp
Normal 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
485
src/desktop/view/Popup.cpp
Normal 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
106
src/desktop/view/Popup.hpp
Normal 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);
|
||||
};
|
||||
}
|
||||
74
src/desktop/view/SessionLock.cpp
Normal file
74
src/desktop/view/SessionLock.cpp
Normal 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;
|
||||
}
|
||||
40
src/desktop/view/SessionLock.hpp
Normal file
40
src/desktop/view/SessionLock.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
262
src/desktop/view/Subsurface.cpp
Normal file
262
src/desktop/view/Subsurface.cpp
Normal 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;
|
||||
}
|
||||
77
src/desktop/view/Subsurface.hpp
Normal file
77
src/desktop/view/Subsurface.hpp
Normal 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
16
src/desktop/view/View.cpp
Normal 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
32
src/desktop/view/View.hpp
Normal 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;
|
||||
};
|
||||
};
|
||||
194
src/desktop/view/WLSurface.cpp
Normal file
194
src/desktop/view/WLSurface.cpp
Normal 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;
|
||||
}
|
||||
118
src/desktop/view/WLSurface.hpp
Normal file
118
src/desktop/view/WLSurface.hpp
Normal 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
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
454
src/desktop/view/Window.hpp
Normal 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, "]");
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue