mirror of
https://github.com/hyprwm/Hyprland.git
synced 2026-08-24 10:04:19 -05:00
protocols: implement ext-background-effect-v1 protocol (#13211)
* feat: implement ext-background-effect-v1 protocol * chore: format * build: unvendorize ext-background-effect protocol * refactor: sync with upstream changes
This commit is contained in:
@@ -590,6 +590,7 @@ protocolnew("staging/fifo" "fifo-v1" false)
|
||||
protocolnew("staging/commit-timing" "commit-timing-v1" false)
|
||||
protocolnew("staging/ext-image-capture-source" "ext-image-capture-source-v1" false)
|
||||
protocolnew("staging/ext-image-copy-capture" "ext-image-copy-capture-v1" false)
|
||||
protocolnew("staging/ext-background-effect" "ext-background-effect-v1" false)
|
||||
|
||||
protocolwayland()
|
||||
|
||||
|
||||
@@ -85,6 +85,10 @@ namespace Desktop::View {
|
||||
float m_overallOpacity = 1.F;
|
||||
CRegion m_visibleRegion;
|
||||
|
||||
// used by the ext-background-effect protocol
|
||||
bool m_hasBackgroundEffect = false;
|
||||
CRegion m_blurRegion;
|
||||
|
||||
struct {
|
||||
CSignalT<> destroy;
|
||||
} m_events;
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "../protocols/HyprlandSurface.hpp"
|
||||
#include "../protocols/ImageCaptureSource.hpp"
|
||||
#include "../protocols/ImageCopyCapture.hpp"
|
||||
#include "../protocols/BackgroundEffect.hpp"
|
||||
#include "../protocols/core/Seat.hpp"
|
||||
#include "../protocols/core/DataDevice.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
@@ -199,6 +200,7 @@ CProtocolManager::CProtocolManager() {
|
||||
PROTO::fifo = makeUnique<CFifoProtocol>(&wp_fifo_manager_v1_interface, 1, "Fifo");
|
||||
PROTO::xdgForeignExporter = makeUnique<CXDGForeignExporterProtocolV2>(&zxdg_exporter_v2_interface, 1, "XDGForeignExporter");
|
||||
PROTO::xdgForeignImporter = makeUnique<CXDGForeignImporterProtocolV2>(&zxdg_importer_v2_interface, 1, "XDGForeignImporter");
|
||||
PROTO::backgroundEffect = makeUnique<CBackgroundEffectProtocol>(&ext_background_effect_manager_v1_interface, 1, "BackgroundEffect");
|
||||
|
||||
if (*PENABLECT)
|
||||
PROTO::commitTiming = makeUnique<CCommitTimingProtocol>(&wp_commit_timing_manager_v1_interface, 1, "CommitTiming");
|
||||
@@ -310,6 +312,7 @@ CProtocolManager::~CProtocolManager() {
|
||||
PROTO::xdgForeignImporter.reset();
|
||||
PROTO::commitTiming.reset();
|
||||
PROTO::imageCaptureSource.reset();
|
||||
PROTO::backgroundEffect.reset();
|
||||
|
||||
for (auto& [_, lease] : PROTO::lease) {
|
||||
lease.reset();
|
||||
@@ -366,6 +369,7 @@ bool CProtocolManager::isGlobalPrivileged(const wl_global* global) {
|
||||
PROTO::commitTiming->getGlobal(),
|
||||
PROTO::xdgForeignExporter->getGlobal(),
|
||||
PROTO::xdgForeignImporter->getGlobal(),
|
||||
PROTO::backgroundEffect->getGlobal(),
|
||||
PROTO::sync ? PROTO::sync->getGlobal() : nullptr,
|
||||
PROTO::mesaDRM ? PROTO::mesaDRM->getGlobal() : nullptr,
|
||||
PROTO::linuxDma ? PROTO::linuxDma->getGlobal() : nullptr,
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
#include "BackgroundEffect.hpp"
|
||||
#include "../desktop/view/WLSurface.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "core/Compositor.hpp"
|
||||
#include "ext-background-effect-v1.hpp"
|
||||
#include <hyprutils/math/Region.hpp>
|
||||
#include <wayland-server.h>
|
||||
|
||||
CBackgroundEffect::CBackgroundEffect(SP<CExtBackgroundEffectSurfaceV1> resource, SP<CWLSurfaceResource> surface) : m_surface(surface) {
|
||||
setResource(std::move(resource));
|
||||
}
|
||||
|
||||
bool CBackgroundEffect::good() const {
|
||||
return m_resource->resource();
|
||||
}
|
||||
|
||||
void CBackgroundEffect::setResource(SP<CExtBackgroundEffectSurfaceV1> resource) {
|
||||
m_resource = std::move(resource);
|
||||
|
||||
if UNLIKELY (!m_resource->resource())
|
||||
return;
|
||||
|
||||
m_resource->setDestroy([this](CExtBackgroundEffectSurfaceV1* resource) { destroy(); });
|
||||
m_resource->setOnDestroy([this](CExtBackgroundEffectSurfaceV1* resource) { destroy(); });
|
||||
|
||||
m_resource->setSetBlurRegion([this](CExtBackgroundEffectSurfaceV1* resource, wl_resource* region) {
|
||||
if UNLIKELY (!m_surface) {
|
||||
m_resource->error(EXT_BACKGROUND_EFFECT_SURFACE_V1_ERROR_SURFACE_DESTROYED, "set_blur_region called for destroyed wl_surface");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!region) {
|
||||
m_blurRegion.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
m_blurRegion = CWLRegionResource::fromResource(region)->m_region;
|
||||
});
|
||||
|
||||
m_listeners.surfaceCommitted = m_surface->m_events.commit.listen([this] {
|
||||
auto hlSurface = Desktop::View::CWLSurface::fromResource(m_surface.lock());
|
||||
|
||||
if (!hlSurface)
|
||||
return;
|
||||
|
||||
if (!m_resource) {
|
||||
// effect was destroyed, clear state on commit per spec
|
||||
hlSurface->m_hasBackgroundEffect = false;
|
||||
hlSurface->m_blurRegion.clear();
|
||||
auto box = hlSurface->getSurfaceBoxGlobal();
|
||||
if (box.has_value())
|
||||
g_pHyprRenderer->damageBox(*box);
|
||||
PROTO::backgroundEffect->destroyEffect(this);
|
||||
return;
|
||||
}
|
||||
|
||||
hlSurface->m_hasBackgroundEffect = true;
|
||||
hlSurface->m_blurRegion = m_blurRegion;
|
||||
auto box = hlSurface->getSurfaceBoxGlobal();
|
||||
|
||||
if (box.has_value())
|
||||
g_pHyprRenderer->damageBox(*box);
|
||||
});
|
||||
|
||||
m_listeners.surfaceDestroyed = m_surface->m_events.destroy.listen([this] { PROTO::backgroundEffect->destroyEffect(this); });
|
||||
}
|
||||
|
||||
void CBackgroundEffect::destroy() {
|
||||
m_resource.reset();
|
||||
m_blurRegion.clear();
|
||||
// The spec requires effect removal to be double-buffered: state is cleared on next wl_surface commit.
|
||||
// If the surface is already destroyed, clean up immediately.
|
||||
if (!m_surface)
|
||||
PROTO::backgroundEffect->destroyEffect(this);
|
||||
}
|
||||
|
||||
CBackgroundEffectProtocol::CBackgroundEffectProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
;
|
||||
}
|
||||
|
||||
void CBackgroundEffectProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
auto manager = m_managers.emplace_back(makeUnique<CExtBackgroundEffectManagerV1>(client, ver, id)).get();
|
||||
manager->setOnDestroy([this](CExtBackgroundEffectManagerV1* manager) { destroyManager(manager); });
|
||||
manager->setDestroy([this](CExtBackgroundEffectManagerV1* manager) { destroyManager(manager); });
|
||||
|
||||
manager->sendCapabilities(EXT_BACKGROUND_EFFECT_MANAGER_V1_CAPABILITY_BLUR);
|
||||
|
||||
manager->setGetBackgroundEffect(
|
||||
[this](CExtBackgroundEffectManagerV1* manager, uint32_t id, wl_resource* surface) { getBackgroundEffect(manager, id, CWLSurfaceResource::fromResource(surface)); });
|
||||
}
|
||||
|
||||
void CBackgroundEffectProtocol::destroyManager(CExtBackgroundEffectManagerV1* manager) {
|
||||
std::erase_if(m_managers, [&](const auto& p) { return p.get() == manager; });
|
||||
}
|
||||
|
||||
void CBackgroundEffectProtocol::destroyEffect(CBackgroundEffect* effect) {
|
||||
std::erase_if(m_effects, [&](const auto& entry) { return entry.second.get() == effect; });
|
||||
}
|
||||
|
||||
void CBackgroundEffectProtocol::getBackgroundEffect(CExtBackgroundEffectManagerV1* manager, uint32_t id, SP<CWLSurfaceResource> surface) {
|
||||
CBackgroundEffect* effect = nullptr;
|
||||
auto iter = std::ranges::find_if(m_effects, [&](const auto& entry) { return entry.second->m_surface == surface; });
|
||||
|
||||
if (iter != m_effects.end()) {
|
||||
if (iter->second->m_resource) {
|
||||
LOGM(Log::ERR, "BackgroundEffect already present for surface {:x}", (uintptr_t)surface.get());
|
||||
manager->error(EXT_BACKGROUND_EFFECT_MANAGER_V1_ERROR_BACKGROUND_EFFECT_EXISTS, "BackgroundEffect already present");
|
||||
return;
|
||||
} else {
|
||||
iter->second->setResource(makeShared<CExtBackgroundEffectSurfaceV1>(manager->client(), manager->version(), id));
|
||||
effect = iter->second.get();
|
||||
}
|
||||
} else {
|
||||
effect = m_effects.emplace(surface, makeUnique<CBackgroundEffect>(makeShared<CExtBackgroundEffectSurfaceV1>(manager->client(), manager->version(), id), surface))
|
||||
.first->second.get();
|
||||
}
|
||||
|
||||
if UNLIKELY (!effect->good()) {
|
||||
manager->noMemory();
|
||||
m_effects.erase(surface);
|
||||
return;
|
||||
}
|
||||
|
||||
auto hlSurface = Desktop::View::CWLSurface::fromResource(surface);
|
||||
if (hlSurface)
|
||||
hlSurface->m_hasBackgroundEffect = true;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <hyprutils/math/Region.hpp>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "WaylandProtocol.hpp"
|
||||
#include "ext-background-effect-v1.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
|
||||
class CWLSurfaceResource;
|
||||
class CBackgroundEffectProtocol;
|
||||
|
||||
class CBackgroundEffect {
|
||||
public:
|
||||
CBackgroundEffect(SP<CExtBackgroundEffectSurfaceV1> resource, SP<CWLSurfaceResource> surface);
|
||||
|
||||
bool good() const;
|
||||
void setResource(SP<CExtBackgroundEffectSurfaceV1> resource);
|
||||
|
||||
private:
|
||||
SP<CExtBackgroundEffectSurfaceV1> m_resource;
|
||||
WP<CWLSurfaceResource> m_surface;
|
||||
CRegion m_blurRegion;
|
||||
|
||||
void destroy();
|
||||
|
||||
struct {
|
||||
CHyprSignalListener surfaceCommitted;
|
||||
CHyprSignalListener surfaceDestroyed;
|
||||
} m_listeners;
|
||||
|
||||
friend class CBackgroundEffectProtocol;
|
||||
};
|
||||
|
||||
class CBackgroundEffectProtocol : public IWaylandProtocol {
|
||||
public:
|
||||
CBackgroundEffectProtocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
|
||||
|
||||
private:
|
||||
void destroyManager(CExtBackgroundEffectManagerV1* res);
|
||||
void destroyEffect(CBackgroundEffect* effect);
|
||||
void getBackgroundEffect(CExtBackgroundEffectManagerV1* manager, uint32_t id, SP<CWLSurfaceResource> surface);
|
||||
|
||||
std::vector<UP<CExtBackgroundEffectManagerV1>> m_managers;
|
||||
std::unordered_map<WP<CWLSurfaceResource>, UP<CBackgroundEffect>> m_effects;
|
||||
|
||||
friend class CBackgroundEffect;
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline UP<CBackgroundEffectProtocol> backgroundEffect;
|
||||
}
|
||||
+14
-1
@@ -1990,6 +1990,19 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<ITexture> tex, const CBox
|
||||
|
||||
static auto PBLURIGNOREOPACITY = CConfigValue<Config::INTEGER>("decoration:blur:ignore_opacity");
|
||||
|
||||
// handle ext-background-effect-v1 blur region if specified
|
||||
CRegion blurClipRegion = data.clipRegion;
|
||||
auto PSURFACE = Desktop::View::CWLSurface::fromResource(data.surface);
|
||||
if (PSURFACE && PSURFACE->m_hasBackgroundEffect && !PSURFACE->m_blurRegion.empty()) {
|
||||
CRegion protocolBlur = PSURFACE->m_blurRegion.copy();
|
||||
protocolBlur.scale(m_renderData.pMonitor->m_scale);
|
||||
protocolBlur.translate(box.pos());
|
||||
if (blurClipRegion.empty())
|
||||
blurClipRegion = protocolBlur;
|
||||
else
|
||||
blurClipRegion.intersect(protocolBlur);
|
||||
}
|
||||
|
||||
g_pHyprRenderer->pushMonitorTransformEnabled(true);
|
||||
bool renderModif = g_pHyprRenderer->m_renderData.renderModif.enabled;
|
||||
if (!data.blockBlurOptimization)
|
||||
@@ -2008,7 +2021,7 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<ITexture> tex, const CBox
|
||||
.wrapY = data.wrapY,
|
||||
.discardMode = data.discardMode,
|
||||
.discardOpacity = data.discardOpacity,
|
||||
.clipRegion = data.clipRegion,
|
||||
.clipRegion = blurClipRegion,
|
||||
.currentLS = data.currentLS,
|
||||
|
||||
.primarySurfaceUVTopLeft = monitorSpaceBox.pos() / m_renderData.pMonitor->m_transformedSize,
|
||||
|
||||
+21
-4
@@ -3322,16 +3322,33 @@ bool IHyprRenderer::shouldBlur(PHLLS ls) {
|
||||
return false;
|
||||
|
||||
static auto PBLUR = CConfigValue<Config::INTEGER>("decoration:blur:enabled");
|
||||
return *PBLUR && ls->m_ruleApplicator->blur().valueOrDefault();
|
||||
if (!*PBLUR)
|
||||
return false;
|
||||
|
||||
auto surface = ls->wlSurface();
|
||||
if (surface && surface->m_hasBackgroundEffect)
|
||||
return !surface->m_blurRegion.empty();
|
||||
|
||||
return ls->m_ruleApplicator->blur().valueOrDefault();
|
||||
}
|
||||
|
||||
bool IHyprRenderer::shouldBlur(PHLWINDOW w) {
|
||||
if (m_bRenderingSnapshot)
|
||||
return false;
|
||||
|
||||
static auto PBLUR = CConfigValue<Config::INTEGER>("decoration:blur:enabled");
|
||||
const bool DONT_BLUR = w->m_ruleApplicator->noBlur().valueOrDefault() || w->m_ruleApplicator->RGBX().valueOrDefault() || w->opaque();
|
||||
return *PBLUR && !DONT_BLUR;
|
||||
static auto PBLUR = CConfigValue<Config::INTEGER>("decoration:blur:enabled");
|
||||
if (!*PBLUR)
|
||||
return false;
|
||||
|
||||
const bool DONT_BLUR = w->m_ruleApplicator->noBlur().valueOrDefault() || w->m_ruleApplicator->RGBX().valueOrDefault() || w->opaque();
|
||||
if (DONT_BLUR)
|
||||
return false;
|
||||
|
||||
auto surface = w->wlSurface();
|
||||
if (surface && surface->m_hasBackgroundEffect)
|
||||
return !surface->m_blurRegion.empty();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IHyprRenderer::shouldBlur(WP<Desktop::View::CPopup> p) {
|
||||
|
||||
Reference in New Issue
Block a user