diff --git a/CMakeLists.txt b/CMakeLists.txt index d0af966c..b530b86b 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,7 @@ protocolNew("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false) protocolNew("unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" "idle-inhibit-unstable-v1" false) protocolNew("unstable/relative-pointer/relative-pointer-unstable-v1.xml" "relative-pointer-unstable-v1" false) protocolNew("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" "xdg-decoration-unstable-v1" false) +protocolNew("staging/alpha-modifier/alpha-modifier-v1.xml" "alpha-modifier-v1" false) # tools add_subdirectory(hyprctl) diff --git a/protocols/meson.build b/protocols/meson.build index 842fe9e9..8247d26a 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -46,6 +46,7 @@ new_protocols = [ [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], [wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'], + [wl_protocol_dir, 'staging/alpha-modifier/alpha-modifier-v1.xml'], ] wl_protos_src = [] diff --git a/src/desktop/WLSurface.hpp b/src/desktop/WLSurface.hpp index b9306eea..a47b2bdd 100644 --- a/src/desktop/WLSurface.hpp +++ b/src/desktop/WLSurface.hpp @@ -81,6 +81,9 @@ class CWLSurface { return (CWLSurface*)pSurface->data; } + // used by the alpha-modifier protocol + float m_pAlphaModifier = 1.F; + private: bool m_bInert = true; diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 47ebbbe0..368c7bb2 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -7,6 +7,7 @@ #include "../protocols/IdleInhibit.hpp" #include "../protocols/RelativePointer.hpp" #include "../protocols/XDGDecoration.hpp" +#include "../protocols/AlphaModifier.hpp" #include "tearing-control-v1.hpp" #include "fractional-scale-v1.hpp" @@ -15,6 +16,7 @@ #include "idle-inhibit-unstable-v1.hpp" #include "relative-pointer-unstable-v1.hpp" #include "xdg-decoration-unstable-v1.hpp" +#include "alpha-modifier-v1.hpp" CProtocolManager::CProtocolManager() { @@ -25,6 +27,7 @@ CProtocolManager::CProtocolManager() { PROTO::idleInhibit = std::make_unique(&zwp_idle_inhibit_manager_v1_interface, 1, "IdleInhibit"); PROTO::relativePointer = std::make_unique(&zwp_relative_pointer_manager_v1_interface, 1, "RelativePointer"); PROTO::xdgDecoration = std::make_unique(&zxdg_decoration_manager_v1_interface, 1, "XDGDecoration"); + PROTO::alphaModifier = std::make_unique(&wp_alpha_modifier_v1_interface, 1, "AlphaModifier"); // Old protocol implementations. // TODO: rewrite them to use hyprwayland-scanner. diff --git a/src/protocols/AlphaModifier.cpp b/src/protocols/AlphaModifier.cpp new file mode 100644 index 00000000..12bc3512 --- /dev/null +++ b/src/protocols/AlphaModifier.cpp @@ -0,0 +1,121 @@ +#include "AlphaModifier.hpp" +#include +#include "../desktop/WLSurface.hpp" +#include "../render/Renderer.hpp" + +CAlphaModifier::CAlphaModifier(SP resource_, wlr_surface* surface_) : resource(resource_), pSurface(surface_) { + if (!resource->resource()) + return; + + resource->setDestroy([this](CWpAlphaModifierSurfaceV1* pMgr) { + PROTO::alphaModifier->destroyModifier(this); + setSurfaceAlpha(1.F); + }); + resource->setOnDestroy([this](CWpAlphaModifierSurfaceV1* pMgr) { + PROTO::alphaModifier->destroyModifier(this); + setSurfaceAlpha(1.F); + }); + + hyprListener_surfaceDestroy.initCallback( + &surface_->events.destroy, [this](void* owner, void* data) { onSurfaceDestroy(); }, this, "CAlphaModifier"); + + resource->setSetMultiplier([this](CWpAlphaModifierSurfaceV1* mod, uint32_t alpha) { + if (!pSurface) { + wl_resource_post_error(mod->resource(), WP_ALPHA_MODIFIER_SURFACE_V1_ERROR_NO_SURFACE, "Surface is gone"); + return; + } + + float a = alpha / (float)UINT32_MAX; + + setSurfaceAlpha(a); + }); +} + +CAlphaModifier::~CAlphaModifier() { + hyprListener_surfaceDestroy.removeCallback(); +} + +bool CAlphaModifier::good() { + return resource->resource(); +} + +wlr_surface* CAlphaModifier::getSurface() { + return pSurface; +} + +void CAlphaModifier::setSurfaceAlpha(float a) { + CWLSurface* surf = CWLSurface::surfaceFromWlr(pSurface); + + if (!surf) { + Debug::log(ERR, "Error in CAlphaModifier::setSurfaceAlpha: No CWLSurface for given surface??"); + return; + } + + surf->m_pAlphaModifier = a; + + auto SURFBOX = surf->getSurfaceBoxGlobal(); + if (SURFBOX.has_value()) + g_pHyprRenderer->damageBox(&*SURFBOX); +} + +void CAlphaModifier::onSurfaceDestroy() { + hyprListener_surfaceDestroy.removeCallback(); + pSurface = nullptr; +} + +CAlphaModifierProtocol::CAlphaModifierProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CAlphaModifierProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(std::make_unique(client, ver, id)).get(); + RESOURCE->setOnDestroy([this](CWpAlphaModifierV1* p) { this->onManagerResourceDestroy(p->resource()); }); + + RESOURCE->setDestroy([this](CWpAlphaModifierV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); + RESOURCE->setGetSurface([this](CWpAlphaModifierV1* pMgr, uint32_t id, wl_resource* surface) { this->onGetSurface(pMgr, id, wlr_surface_from_resource(surface)); }); +} + +void CAlphaModifierProtocol::onManagerResourceDestroy(wl_resource* res) { + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); +} + +void CAlphaModifierProtocol::destroyModifier(CAlphaModifier* modifier) { + if (modifier->getSurface()) + m_mAlphaModifiers.erase(modifier->getSurface()); + else { + // find it first + wlr_surface* deadptr = nullptr; + for (auto& [k, v] : m_mAlphaModifiers) { + if (v.get() == modifier) { + deadptr = k; + break; + } + } + + if (!deadptr) { + Debug::log(ERR, "CAlphaModifierProtocol::destroyModifier: dead resource but no deadptr???"); + return; + } + + m_mAlphaModifiers.erase(deadptr); + } +} + +void CAlphaModifierProtocol::onGetSurface(CWpAlphaModifierV1* pMgr, uint32_t id, wlr_surface* surface) { + if (m_mAlphaModifiers.contains(surface)) { + wl_resource_post_error(pMgr->resource(), WP_ALPHA_MODIFIER_V1_ERROR_ALREADY_CONSTRUCTED, "AlphaModifier already present"); + return; + } + + const auto CLIENT = wl_resource_get_client(pMgr->resource()); + const auto RESOURCE = + m_mAlphaModifiers + .emplace(surface, std::make_unique(std::make_shared(CLIENT, wl_resource_get_version(pMgr->resource()), id), surface)) + .first->second.get(); + + if (!RESOURCE->good()) { + wl_resource_post_no_memory(pMgr->resource()); + m_mAlphaModifiers.erase(surface); + return; + } +} \ No newline at end of file diff --git a/src/protocols/AlphaModifier.hpp b/src/protocols/AlphaModifier.hpp new file mode 100644 index 00000000..292c2e14 --- /dev/null +++ b/src/protocols/AlphaModifier.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "alpha-modifier-v1.hpp" + +class CAlphaModifier { + public: + CAlphaModifier(SP resource_, wlr_surface* surface); + ~CAlphaModifier(); + + bool good(); + wlr_surface* getSurface(); + void onSurfaceDestroy(); + + private: + SP resource; + wlr_surface* pSurface = nullptr; + + void setSurfaceAlpha(float a); + + DYNLISTENER(surfaceDestroy); +}; + +class CAlphaModifierProtocol : public IWaylandProtocol { + public: + CAlphaModifierProtocol(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 onManagerResourceDestroy(wl_resource* res); + void destroyModifier(CAlphaModifier* decoration); + void onGetSurface(CWpAlphaModifierV1* pMgr, uint32_t id, wlr_surface* surface); + + // + std::vector> m_vManagers; + std::unordered_map> m_mAlphaModifiers; // xdg_toplevel -> deco + + friend class CAlphaModifier; +}; + +namespace PROTO { + inline UP alphaModifier; +}; \ No newline at end of file diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index a51901b4..957d3263 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -96,12 +96,15 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) double outputX = 0, outputY = 0; wlr_output_layout_output_coords(g_pCompositor->m_sWLROutputLayout, RDATA->pMonitor->output, &outputX, &outputY); + auto* const PSURFACE = CWLSurface::surfaceFromWlr(surface); + + const float ALPHA = RDATA->alpha * RDATA->fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F); + CBox windowBox; if (RDATA->surface && surface == RDATA->surface) { windowBox = {(int)outputX + RDATA->x + x, (int)outputY + RDATA->y + y, RDATA->w, RDATA->h}; // however, if surface buffer w / h < box, we need to adjust them - auto* const PSURFACE = CWLSurface::surfaceFromWlr(surface); const auto PWINDOW = PSURFACE ? PSURFACE->getWindow() : nullptr; if (PSURFACE && !PSURFACE->m_bFillIgnoreSmall && PSURFACE->small() /* guarantees PWINDOW */) { @@ -175,7 +178,7 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) rounding = 0; const bool WINDOWOPAQUE = RDATA->pWindow && RDATA->pWindow->m_pWLSurface.wlr() == surface ? RDATA->pWindow->opaque() : false; - const bool CANDISABLEBLEND = RDATA->alpha * RDATA->fadeAlpha >= 1.f && rounding == 0 && (WINDOWOPAQUE || surface->opaque); + const bool CANDISABLEBLEND = ALPHA >= 1.f && rounding == 0 && (WINDOWOPAQUE || surface->opaque); if (CANDISABLEBLEND) g_pHyprOpenGL->blend(false); @@ -183,19 +186,19 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) g_pHyprOpenGL->blend(true); if (RDATA->surface && surface == RDATA->surface) { - if (wlr_xwayland_surface_try_from_wlr_surface(surface) && !wlr_xwayland_surface_try_from_wlr_surface(surface)->has_alpha && RDATA->fadeAlpha * RDATA->alpha == 1.f) { - g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, RDATA->fadeAlpha * RDATA->alpha, rounding, true); + if (wlr_xwayland_surface_try_from_wlr_surface(surface) && !wlr_xwayland_surface_try_from_wlr_surface(surface)->has_alpha && ALPHA == 1.f) { + g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, true); } else { if (RDATA->blur) - g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, RDATA->fadeAlpha * RDATA->alpha, surface, rounding, RDATA->blockBlurOptimization, RDATA->fadeAlpha); + g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, surface, rounding, RDATA->blockBlurOptimization, RDATA->fadeAlpha); else - g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, RDATA->fadeAlpha * RDATA->alpha, rounding, true); + g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, true); } } else { if (RDATA->blur && RDATA->popup) - g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, RDATA->fadeAlpha * RDATA->alpha, surface, rounding, true, RDATA->fadeAlpha); + g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, surface, rounding, true, RDATA->fadeAlpha); else - g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, RDATA->fadeAlpha * RDATA->alpha, rounding, true); + g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, true); } if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) {