mirror of
https://github.com/hyprwm/hyprlock.git
synced 2025-12-20 00:40:03 +01:00
core: use a screenshot for fade in/out (#726)
This commit is contained in:
parent
e67036e8cc
commit
a9638986c3
13 changed files with 252 additions and 229 deletions
|
|
@ -6,6 +6,7 @@
|
|||
#include "../auth/Auth.hpp"
|
||||
#include "../auth/Fingerprint.hpp"
|
||||
#include "Egl.hpp"
|
||||
#include <chrono>
|
||||
#include <hyprutils/memory/UniquePtr.hpp>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/poll.h>
|
||||
|
|
@ -313,18 +314,52 @@ void CHyprlock::run() {
|
|||
|
||||
Debug::log(LOG, "Running on {}", m_sCurrentDesktop);
|
||||
|
||||
// Hyprland violates the protocol a bit to allow for this.
|
||||
if (m_sCurrentDesktop != "Hyprland") {
|
||||
if (!g_pHyprlock->m_bImmediateRender) {
|
||||
// Gather background resources and screencopy frames before locking the screen.
|
||||
// We need to do this because as soon as we lock the screen, workspaces frames can no longer be captured. It either won't work at all, or we will capture hyprlock itself.
|
||||
// Bypass with --immediate-render (can cause the background first rendering a solid color and missing or inaccurate screencopy frames)
|
||||
const auto MAXDELAYMS = 2000; // 2 Seconds
|
||||
const auto STARTGATHERTP = std::chrono::system_clock::now();
|
||||
|
||||
int fdcount = 1;
|
||||
pollfd pollfds[2];
|
||||
pollfds[0] = {
|
||||
.fd = wl_display_get_fd(m_sWaylandState.display),
|
||||
.events = POLLIN,
|
||||
};
|
||||
|
||||
if (g_pRenderer->asyncResourceGatherer->gatheredEventfd.isValid()) {
|
||||
pollfds[1] = {
|
||||
.fd = g_pRenderer->asyncResourceGatherer->gatheredEventfd.get(),
|
||||
.events = POLLIN,
|
||||
};
|
||||
|
||||
fdcount++;
|
||||
}
|
||||
|
||||
while (!g_pRenderer->asyncResourceGatherer->gathered) {
|
||||
wl_display_flush(m_sWaylandState.display);
|
||||
if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
|
||||
if (poll(pollfds, fdcount, /* 100ms timeout */ 100) < 0) {
|
||||
RASSERT(errno == EINTR, "[core] Polling fds failed with {}", errno);
|
||||
wl_display_cancel_read(m_sWaylandState.display);
|
||||
continue;
|
||||
}
|
||||
wl_display_read_events(m_sWaylandState.display);
|
||||
wl_display_dispatch_pending(m_sWaylandState.display);
|
||||
} else {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
wl_display_dispatch(m_sWaylandState.display);
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - STARTGATHERTP).count() > MAXDELAYMS) {
|
||||
Debug::log(WARN, "Gathering resources timed out after {} milliseconds. Backgrounds may be delayed and render `background:color` at first.", MAXDELAYMS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Debug::log(LOG, "Resources gathered after {} milliseconds",
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - STARTGATHERTP).count());
|
||||
}
|
||||
|
||||
// Failed to lock the session
|
||||
|
|
@ -499,16 +534,13 @@ void CHyprlock::unlock() {
|
|||
return;
|
||||
}
|
||||
|
||||
const bool IMMEDIATE = m_sCurrentDesktop != "Hyprland";
|
||||
|
||||
g_pRenderer->startFadeOut(true, IMMEDIATE);
|
||||
m_bUnlockedCalled = true;
|
||||
g_pRenderer->startFadeOut(true);
|
||||
|
||||
renderAllOutputs();
|
||||
}
|
||||
|
||||
bool CHyprlock::isUnlocked() {
|
||||
return m_bUnlockedCalled || m_bTerminate;
|
||||
return !m_bLocked;
|
||||
}
|
||||
|
||||
void CHyprlock::clearPasswordBuffer() {
|
||||
|
|
|
|||
|
|
@ -158,8 +158,6 @@ class CHyprlock {
|
|||
bool timerEvent = false;
|
||||
} m_sLoopState;
|
||||
|
||||
bool m_bUnlockedCalled = false;
|
||||
|
||||
std::vector<std::shared_ptr<CTimer>> m_vTimers;
|
||||
|
||||
std::vector<uint32_t> m_vPressedKeys;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
#include "AsyncResourceGatherer.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../core/Egl.hpp"
|
||||
#include <cairo/cairo.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "../helpers/Color.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
#include "src/helpers/Color.hpp"
|
||||
#include "src/helpers/Log.hpp"
|
||||
#include <algorithm>
|
||||
#include <cairo/cairo.h>
|
||||
#include <filesystem>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <hyprgraphics/image/Image.hpp>
|
||||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
using namespace Hyprgraphics;
|
||||
using namespace Hyprutils::OS;
|
||||
|
||||
CAsyncResourceGatherer::CAsyncResourceGatherer() {
|
||||
if (g_pHyprlock->getScreencopy())
|
||||
|
|
@ -18,45 +21,39 @@ CAsyncResourceGatherer::CAsyncResourceGatherer() {
|
|||
|
||||
initialGatherThread = std::thread([this]() { this->gather(); });
|
||||
asyncLoopThread = std::thread([this]() { this->asyncAssetSpinLock(); });
|
||||
|
||||
gatheredEventfd = CFileDescriptor{eventfd(0, EFD_CLOEXEC)};
|
||||
if (!gatheredEventfd.isValid())
|
||||
Debug::log(ERR, "Failed to create eventfd: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void CAsyncResourceGatherer::enqueueScreencopyFrames() {
|
||||
// some things can't be done async :(
|
||||
// gather background textures when needed
|
||||
const auto FADEINCFG = g_pConfigManager->m_AnimationTree.getConfig("fadeIn");
|
||||
const auto FADEOUTCFG = g_pConfigManager->m_AnimationTree.getConfig("fadeOut");
|
||||
const auto BGSCREENSHOT = std::ranges::any_of(g_pConfigManager->getWidgetConfigs(), [](const auto& w) { //
|
||||
return w.type == "background" && std::string{std::any_cast<Hyprlang::STRING>(w.values.at("path"))} == "screenshot";
|
||||
});
|
||||
|
||||
const auto CWIDGETS = g_pConfigManager->getWidgetConfigs();
|
||||
// No screenshot background AND no fade in AND no fade out -> we don't need screencopy
|
||||
if (!BGSCREENSHOT && (!FADEINCFG->pValues || !FADEINCFG->pValues->internalEnabled) && //
|
||||
(!FADEOUTCFG->pValues || !FADEOUTCFG->pValues->internalEnabled))
|
||||
return;
|
||||
|
||||
std::vector<std::string> mons;
|
||||
|
||||
for (auto& c : CWIDGETS) {
|
||||
if (c.type != "background")
|
||||
continue;
|
||||
|
||||
if (std::string{std::any_cast<Hyprlang::STRING>(c.values.at("path"))} != "screenshot")
|
||||
continue;
|
||||
|
||||
// mamma mia
|
||||
if (c.monitor.empty()) {
|
||||
mons.clear();
|
||||
for (auto& m : g_pHyprlock->m_vOutputs) {
|
||||
mons.push_back(m->stringPort);
|
||||
}
|
||||
break;
|
||||
} else
|
||||
mons.push_back(c.monitor);
|
||||
}
|
||||
|
||||
for (auto& mon : mons) {
|
||||
const auto MON = std::ranges::find_if(g_pHyprlock->m_vOutputs, [mon](const auto& other) { return other->stringPort == mon || other->stringDesc.starts_with(mon); });
|
||||
|
||||
if (MON == g_pHyprlock->m_vOutputs.end())
|
||||
continue;
|
||||
|
||||
scframes.emplace_back(makeUnique<CScreencopyFrame>(*MON));
|
||||
for (const auto& MON : g_pHyprlock->m_vOutputs) {
|
||||
scframes.emplace_back(makeUnique<CScreencopyFrame>(MON));
|
||||
}
|
||||
}
|
||||
|
||||
SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
|
||||
if (id.contains(CScreencopyFrame::RESOURCEIDPREFIX)) {
|
||||
for (auto& frame : scframes) {
|
||||
if (id == frame->m_resourceID)
|
||||
return frame->m_asset.ready ? &frame->m_asset : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto& a : assets) {
|
||||
if (a.first == id)
|
||||
return &a.second;
|
||||
|
|
@ -69,16 +66,10 @@ SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
|
|||
}
|
||||
};
|
||||
|
||||
for (auto& frame : scframes) {
|
||||
if (id == frame->m_resourceID)
|
||||
return frame->m_asset.ready ? &frame->m_asset : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static SP<CCairoSurface> getCairoSurfaceFromImageFile(const std::filesystem::path& path) {
|
||||
|
||||
auto image = CImage(path);
|
||||
if (!image.success()) {
|
||||
Debug::log(ERR, "Image {} could not be loaded: {}", path.string(), image.getError());
|
||||
|
|
@ -131,6 +122,9 @@ void CAsyncResourceGatherer::gather() {
|
|||
}
|
||||
|
||||
gathered = true;
|
||||
// wake hyprlock from poll
|
||||
if (gatheredEventfd.isValid())
|
||||
eventfd_write(gatheredEventfd.get(), 1);
|
||||
}
|
||||
|
||||
bool CAsyncResourceGatherer::apply() {
|
||||
|
|
|
|||
|
|
@ -9,13 +9,15 @@
|
|||
#include <any>
|
||||
#include "Shared.hpp"
|
||||
#include <hyprgraphics/cairo/CairoSurface.hpp>
|
||||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
|
||||
class CAsyncResourceGatherer {
|
||||
public:
|
||||
CAsyncResourceGatherer();
|
||||
std::atomic<bool> gathered = false;
|
||||
std::atomic<bool> gathered = false;
|
||||
Hyprutils::OS::CFileDescriptor gatheredEventfd;
|
||||
|
||||
std::atomic<float> progress = 0;
|
||||
std::atomic<float> progress = 0;
|
||||
|
||||
/* only call from ogl thread */
|
||||
SPreloadedAsset* getAssetByID(const std::string& id);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "Framebuffer.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <utility>
|
||||
|
||||
static uint32_t drmFormatToGL(uint32_t drm) {
|
||||
switch (drm) {
|
||||
|
|
@ -97,7 +99,7 @@ void CFramebuffer::bind() const {
|
|||
glViewport(0, 0, m_vSize.x, m_vSize.y);
|
||||
}
|
||||
|
||||
void CFramebuffer::release() {
|
||||
void CFramebuffer::destroyBuffer() {
|
||||
if (m_iFb != (uint32_t)-1 && m_iFb)
|
||||
glDeleteFramebuffers(1, &m_iFb);
|
||||
|
||||
|
|
@ -114,7 +116,7 @@ void CFramebuffer::release() {
|
|||
}
|
||||
|
||||
CFramebuffer::~CFramebuffer() {
|
||||
release();
|
||||
destroyBuffer();
|
||||
}
|
||||
|
||||
bool CFramebuffer::isAllocated() const {
|
||||
|
|
|
|||
|
|
@ -8,17 +8,19 @@ class CFramebuffer {
|
|||
public:
|
||||
~CFramebuffer();
|
||||
|
||||
bool alloc(int w, int h, bool highres = false);
|
||||
void addStencil();
|
||||
void bind() const;
|
||||
void release();
|
||||
void reset();
|
||||
bool isAllocated() const;
|
||||
bool alloc(int w, int h, bool highres = false);
|
||||
void addStencil();
|
||||
void bind() const;
|
||||
void destroyBuffer();
|
||||
bool isAllocated() const;
|
||||
|
||||
Vector2D m_vSize;
|
||||
Vector2D m_vSize;
|
||||
|
||||
CTexture m_cTex;
|
||||
GLuint m_iFb = -1;
|
||||
CTexture m_cTex;
|
||||
GLuint m_iFb = -1;
|
||||
|
||||
CTexture* m_pStencilTex = nullptr;
|
||||
CTexture* m_pStencilTex = nullptr;
|
||||
|
||||
CFramebuffer& operator=(CFramebuffer&&) = delete;
|
||||
CFramebuffer& operator=(const CFramebuffer&) = delete;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -613,11 +613,8 @@ void CRenderer::startFadeIn() {
|
|||
opacity->setCallbackOnEnd([this](auto) { opacity->setConfig(g_pConfigManager->m_AnimationTree.getConfig("fadeOut")); }, true);
|
||||
}
|
||||
|
||||
void CRenderer::startFadeOut(bool unlock, bool immediate) {
|
||||
if (immediate)
|
||||
opacity->setValueAndWarp(0.f);
|
||||
else
|
||||
*opacity = 0.f;
|
||||
void CRenderer::startFadeOut(bool unlock) {
|
||||
*opacity = 0.f;
|
||||
|
||||
if (unlock)
|
||||
opacity->setCallbackOnEnd([](auto) { g_pHyprlock->releaseSessionLock(); }, true);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class CRenderer {
|
|||
void reconfigureWidgetsFor(OUTPUTID id);
|
||||
|
||||
void startFadeIn();
|
||||
void startFadeOut(bool unlock = false, bool immediate = true);
|
||||
void startFadeOut(bool unlock = false);
|
||||
std::vector<SP<IWidget>>& getOrCreateWidgetsFor(const CSessionLockSurface& surf);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ static PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullpt
|
|||
|
||||
//
|
||||
std::string CScreencopyFrame::getResourceId(SP<COutput> pOutput) {
|
||||
return std::format("screencopy:{}-{}x{}", pOutput->stringPort, pOutput->size.x, pOutput->size.y);
|
||||
return RESOURCEIDPREFIX + std::format(":{}-{}x{}", pOutput->stringPort, pOutput->size.x, pOutput->size.y);
|
||||
}
|
||||
|
||||
CScreencopyFrame::CScreencopyFrame(SP<COutput> pOutput) : m_outputRef(pOutput) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ class ISCFrame {
|
|||
|
||||
class CScreencopyFrame {
|
||||
public:
|
||||
static std::string getResourceId(SP<COutput> pOutput);
|
||||
static std::string getResourceId(SP<COutput> pOutput);
|
||||
static constexpr const std::string RESOURCEIDPREFIX = "screencopy";
|
||||
|
||||
CScreencopyFrame(SP<COutput> pOutput);
|
||||
~CScreencopyFrame() = default;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,23 @@
|
|||
#include "Background.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
#include "../Framebuffer.hpp"
|
||||
#include "../Shared.hpp"
|
||||
#include "../../core/hyprlock.hpp"
|
||||
#include "../../helpers/Log.hpp"
|
||||
#include "../../helpers/MiscFunctions.hpp"
|
||||
#include "../../core/AnimationManager.hpp"
|
||||
#include "../../config/ConfigManager.hpp"
|
||||
#include <chrono>
|
||||
#include <hyprlang.hpp>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <GLES3/gl32.h>
|
||||
|
||||
CBackground::CBackground() {
|
||||
blurredFB = makeUnique<CFramebuffer>();
|
||||
pendingBlurredFB = makeUnique<CFramebuffer>();
|
||||
}
|
||||
|
||||
CBackground::~CBackground() {
|
||||
reset();
|
||||
}
|
||||
|
|
@ -32,7 +41,6 @@ void CBackground::configure(const std::unordered_map<std::string, std::any>& pro
|
|||
path = std::any_cast<Hyprlang::STRING>(props.at("path"));
|
||||
reloadCommand = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd"));
|
||||
reloadTime = std::any_cast<Hyprlang::INT>(props.at("reload_time"));
|
||||
crossFadeTime = std::any_cast<Hyprlang::FLOAT>(props.at("crossfade_time"));
|
||||
|
||||
} catch (const std::bad_any_cast& e) {
|
||||
RASSERT(false, "Failed to construct CBackground: {}", e.what()); //
|
||||
|
|
@ -42,12 +50,15 @@ void CBackground::configure(const std::unordered_map<std::string, std::any>& pro
|
|||
|
||||
isScreenshot = path == "screenshot";
|
||||
|
||||
viewport = pOutput->getViewport();
|
||||
outputPort = pOutput->stringPort;
|
||||
transform = isScreenshot ? wlTransformToHyprutils(invertTransform(pOutput->transform)) : HYPRUTILS_TRANSFORM_NORMAL;
|
||||
viewport = pOutput->getViewport();
|
||||
outputPort = pOutput->stringPort;
|
||||
transform = isScreenshot ? wlTransformToHyprutils(invertTransform(pOutput->transform)) : HYPRUTILS_TRANSFORM_NORMAL;
|
||||
scResourceID = CScreencopyFrame::getResourceId(pOutput);
|
||||
|
||||
g_pAnimationManager->createAnimation(0.f, crossFadeProgress, g_pConfigManager->m_AnimationTree.getConfig("fadeIn"));
|
||||
|
||||
if (isScreenshot) {
|
||||
resourceID = CScreencopyFrame::getResourceId(pOutput);
|
||||
resourceID = scResourceID;
|
||||
// When the initial gather of the asyncResourceGatherer is completed (ready), all DMAFrames are available.
|
||||
// Dynamic ones are tricky, because a screencopy would copy hyprlock itself.
|
||||
if (g_pRenderer->asyncResourceGatherer->gathered) {
|
||||
|
|
@ -78,13 +89,8 @@ void CBackground::reset() {
|
|||
reloadTimer.reset();
|
||||
}
|
||||
|
||||
if (fade) {
|
||||
if (fade->crossFadeTimer) {
|
||||
fade->crossFadeTimer->cancel();
|
||||
fade->crossFadeTimer.reset();
|
||||
}
|
||||
fade.reset();
|
||||
}
|
||||
blurredFB->destroyBuffer();
|
||||
pendingBlurredFB->destroyBuffer();
|
||||
}
|
||||
|
||||
void CBackground::renderRect(CHyprColor color) {
|
||||
|
|
@ -99,98 +105,26 @@ static void onReloadTimer(WP<CBackground> ref) {
|
|||
}
|
||||
}
|
||||
|
||||
static void onCrossFadeTimer(WP<CBackground> ref) {
|
||||
if (auto PBG = ref.lock(); PBG)
|
||||
PBG->onCrossFadeTimerUpdate();
|
||||
}
|
||||
|
||||
static void onAssetCallback(WP<CBackground> ref) {
|
||||
if (auto PBG = ref.lock(); PBG)
|
||||
PBG->startCrossFadeOrUpdateRender();
|
||||
PBG->startCrossFade();
|
||||
}
|
||||
|
||||
bool CBackground::draw(const SRenderData& data) {
|
||||
void CBackground::renderBlur(const CTexture& tex, CFramebuffer& fb) {
|
||||
if (firstRender)
|
||||
firstRender = false;
|
||||
|
||||
if (resourceID.empty()) {
|
||||
CHyprColor col = color;
|
||||
col.a *= data.opacity;
|
||||
renderRect(col);
|
||||
return data.opacity < 1.0;
|
||||
// make it brah
|
||||
Vector2D size = asset->texture.m_vSize;
|
||||
if (transform % 2 == 1 && isScreenshot) {
|
||||
size.x = asset->texture.m_vSize.y;
|
||||
size.y = asset->texture.m_vSize.x;
|
||||
}
|
||||
|
||||
if (!asset)
|
||||
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
|
||||
CBox texbox = {{}, size};
|
||||
|
||||
if (!asset) {
|
||||
CHyprColor col = color;
|
||||
col.a *= data.opacity;
|
||||
renderRect(col);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (asset->texture.m_iType == TEXTURE_INVALID) {
|
||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
||||
resourceID = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fade || ((blurPasses > 0 || isScreenshot) && (!blurredFB.isAllocated() || firstRender))) {
|
||||
|
||||
if (firstRender)
|
||||
firstRender = false;
|
||||
|
||||
// make it brah
|
||||
Vector2D size = asset->texture.m_vSize;
|
||||
if (transform % 2 == 1 && isScreenshot) {
|
||||
size.x = asset->texture.m_vSize.y;
|
||||
size.y = asset->texture.m_vSize.x;
|
||||
}
|
||||
|
||||
CBox texbox = {{}, size};
|
||||
|
||||
float scaleX = viewport.x / size.x;
|
||||
float scaleY = viewport.y / size.y;
|
||||
|
||||
texbox.w *= std::max(scaleX, scaleY);
|
||||
texbox.h *= std::max(scaleX, scaleY);
|
||||
|
||||
if (scaleX > scaleY)
|
||||
texbox.y = -(texbox.h - viewport.y) / 2.f;
|
||||
else
|
||||
texbox.x = -(texbox.w - viewport.x) / 2.f;
|
||||
texbox.round();
|
||||
|
||||
if (!blurredFB.isAllocated())
|
||||
blurredFB.alloc(viewport.x, viewport.y); // TODO 10 bit
|
||||
|
||||
blurredFB.bind();
|
||||
|
||||
if (fade)
|
||||
g_pRenderer->renderTextureMix(texbox, asset->texture, pendingAsset->texture, 1.0,
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - fade->start).count() / (1000 * crossFadeTime), 0,
|
||||
transform);
|
||||
else
|
||||
g_pRenderer->renderTexture(texbox, asset->texture, 1.0, 0, transform);
|
||||
|
||||
if (blurPasses > 0)
|
||||
g_pRenderer->blurFB(blurredFB,
|
||||
CRenderer::SBlurParams{.size = blurSize,
|
||||
.passes = blurPasses,
|
||||
.noise = noise,
|
||||
.contrast = contrast,
|
||||
.brightness = brightness,
|
||||
.vibrancy = vibrancy,
|
||||
.vibrancy_darkness = vibrancy_darkness});
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
CTexture* tex = blurredFB.isAllocated() ? &blurredFB.m_cTex : &asset->texture;
|
||||
|
||||
CBox texbox = {{}, tex->m_vSize};
|
||||
|
||||
Vector2D size = tex->m_vSize;
|
||||
float scaleX = viewport.x / tex->m_vSize.x;
|
||||
float scaleY = viewport.y / tex->m_vSize.y;
|
||||
float scaleX = viewport.x / size.x;
|
||||
float scaleY = viewport.y / size.y;
|
||||
|
||||
texbox.w *= std::max(scaleX, scaleY);
|
||||
texbox.h *= std::max(scaleX, scaleY);
|
||||
|
|
@ -200,9 +134,94 @@ bool CBackground::draw(const SRenderData& data) {
|
|||
else
|
||||
texbox.x = -(texbox.w - viewport.x) / 2.f;
|
||||
texbox.round();
|
||||
g_pRenderer->renderTexture(texbox, *tex, data.opacity, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);
|
||||
|
||||
return fade || data.opacity < 1.0; // actively render during fading
|
||||
if (!fb.isAllocated())
|
||||
fb.alloc(viewport.x, viewport.y); // TODO 10 bit
|
||||
|
||||
fb.bind();
|
||||
|
||||
g_pRenderer->renderTexture(texbox, tex, 1.0, 0, transform);
|
||||
|
||||
if (blurPasses > 0)
|
||||
g_pRenderer->blurFB(fb,
|
||||
CRenderer::SBlurParams{.size = blurSize,
|
||||
.passes = blurPasses,
|
||||
.noise = noise,
|
||||
.contrast = contrast,
|
||||
.brightness = brightness,
|
||||
.vibrancy = vibrancy,
|
||||
.vibrancy_darkness = vibrancy_darkness});
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
static CBox getScaledBoxForTexture(const CTexture& tex, const Vector2D& viewport) {
|
||||
CBox texbox = {{}, tex.m_vSize};
|
||||
|
||||
Vector2D size = tex.m_vSize;
|
||||
float scaleX = viewport.x / tex.m_vSize.x;
|
||||
float scaleY = viewport.y / tex.m_vSize.y;
|
||||
|
||||
texbox.w *= std::max(scaleX, scaleY);
|
||||
texbox.h *= std::max(scaleX, scaleY);
|
||||
|
||||
if (scaleX > scaleY)
|
||||
texbox.y = -(texbox.h - viewport.y) / 2.f;
|
||||
else
|
||||
texbox.x = -(texbox.w - viewport.x) / 2.f;
|
||||
texbox.round();
|
||||
|
||||
return texbox;
|
||||
}
|
||||
|
||||
bool CBackground::draw(const SRenderData& data) {
|
||||
if (!asset && !resourceID.empty())
|
||||
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
|
||||
|
||||
// path=screenshot -> scAsset = asset
|
||||
if (!scAsset)
|
||||
scAsset = (asset && isScreenshot) ? asset : g_pRenderer->asyncResourceGatherer->getAssetByID(scResourceID);
|
||||
|
||||
if (!asset || resourceID.empty()) {
|
||||
// fade in/out with a solid color
|
||||
if (data.opacity < 1.0 && scAsset) {
|
||||
const auto SCTEXBOX = getScaledBoxForTexture(scAsset->texture, viewport);
|
||||
g_pRenderer->renderTexture(SCTEXBOX, scAsset->texture, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);
|
||||
CHyprColor col = color;
|
||||
col.a *= data.opacity;
|
||||
renderRect(col);
|
||||
return true;
|
||||
}
|
||||
|
||||
renderRect(color);
|
||||
return !asset && !resourceID.empty(); // resource not ready
|
||||
}
|
||||
|
||||
if (asset->texture.m_iType == TEXTURE_INVALID) {
|
||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
||||
resourceID = "";
|
||||
renderRect(color);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (asset && (blurPasses > 0 || isScreenshot) && (!blurredFB->isAllocated() || firstRender))
|
||||
renderBlur(asset->texture, *blurredFB);
|
||||
|
||||
// For crossfading a new asset
|
||||
if (pendingAsset && blurPasses > 0 && !pendingBlurredFB->isAllocated())
|
||||
renderBlur(pendingAsset->texture, *pendingBlurredFB);
|
||||
|
||||
const auto& TEX = blurredFB->isAllocated() ? blurredFB->m_cTex : asset->texture;
|
||||
const auto TEXBOX = getScaledBoxForTexture(TEX, viewport);
|
||||
|
||||
if (data.opacity < 1.0 && scAsset)
|
||||
g_pRenderer->renderTextureMix(TEXBOX, scAsset->texture, TEX, 1.0, data.opacity, 0);
|
||||
else if (crossFadeProgress->isBeingAnimated()) {
|
||||
const auto& PENDINGTEX = pendingBlurredFB->isAllocated() ? pendingBlurredFB->m_cTex : pendingAsset->texture;
|
||||
g_pRenderer->renderTextureMix(TEXBOX, TEX, PENDINGTEX, 1.0, crossFadeProgress->value(), 0);
|
||||
} else
|
||||
g_pRenderer->renderTexture(TEXBOX, TEX, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);
|
||||
|
||||
return crossFadeProgress->isBeingAnimated() || data.opacity < 1.0;
|
||||
}
|
||||
|
||||
void CBackground::plantReloadTimer() {
|
||||
|
|
@ -213,27 +232,6 @@ void CBackground::plantReloadTimer() {
|
|||
reloadTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), [REF = m_self](auto, auto) { onReloadTimer(REF); }, nullptr, true);
|
||||
}
|
||||
|
||||
void CBackground::onCrossFadeTimerUpdate() {
|
||||
|
||||
// Animation done: Unload previous asset, deinitialize the fade and pass the asset
|
||||
|
||||
if (fade) {
|
||||
fade->crossFadeTimer.reset();
|
||||
fade.reset();
|
||||
}
|
||||
|
||||
if (blurPasses <= 0 && !isScreenshot)
|
||||
blurredFB.release();
|
||||
|
||||
asset = pendingAsset;
|
||||
resourceID = pendingResourceID;
|
||||
pendingResourceID = "";
|
||||
pendingAsset = nullptr;
|
||||
firstRender = true;
|
||||
|
||||
g_pHyprlock->renderOutput(outputPort);
|
||||
}
|
||||
|
||||
void CBackground::onReloadTimerUpdate() {
|
||||
const std::string OLDPATH = path;
|
||||
|
||||
|
|
@ -279,37 +277,37 @@ void CBackground::onReloadTimerUpdate() {
|
|||
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
|
||||
}
|
||||
|
||||
void CBackground::startCrossFadeOrUpdateRender() {
|
||||
void CBackground::startCrossFade() {
|
||||
auto newAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(pendingResourceID);
|
||||
if (newAsset) {
|
||||
if (newAsset->texture.m_iType == TEXTURE_INVALID) {
|
||||
g_pRenderer->asyncResourceGatherer->unloadAsset(newAsset);
|
||||
Debug::log(ERR, "New asset had an invalid texture!");
|
||||
pendingResourceID = "";
|
||||
} else if (resourceID != pendingResourceID) {
|
||||
pendingAsset = newAsset;
|
||||
if (crossFadeTime > 0) {
|
||||
// Start a fade
|
||||
if (!fade)
|
||||
fade = makeUnique<SFade>(std::chrono::system_clock::now(), 0, nullptr);
|
||||
else {
|
||||
// Maybe we where already fading so reset it just in case, but should'nt be happening.
|
||||
if (fade->crossFadeTimer) {
|
||||
fade->crossFadeTimer->cancel();
|
||||
fade->crossFadeTimer.reset();
|
||||
crossFadeProgress->setValueAndWarp(0);
|
||||
*crossFadeProgress = 1.0;
|
||||
|
||||
crossFadeProgress->setCallbackOnEnd(
|
||||
[REF = m_self](auto) {
|
||||
if (const auto PSELF = REF.lock()) {
|
||||
PSELF->asset = PSELF->pendingAsset;
|
||||
PSELF->pendingAsset = nullptr;
|
||||
g_pRenderer->asyncResourceGatherer->unloadAsset(PSELF->pendingAsset);
|
||||
PSELF->resourceID = PSELF->pendingResourceID;
|
||||
PSELF->pendingResourceID = "";
|
||||
|
||||
PSELF->blurredFB->destroyBuffer();
|
||||
PSELF->blurredFB = std::move(PSELF->pendingBlurredFB);
|
||||
}
|
||||
}
|
||||
fade->start = std::chrono::system_clock::now();
|
||||
fade->a = 0;
|
||||
fade->crossFadeTimer =
|
||||
g_pHyprlock->addTimer(std::chrono::milliseconds((int)(1000.0 * crossFadeTime)), [REF = m_self](auto, auto) { onCrossFadeTimer(REF); }, nullptr);
|
||||
} else {
|
||||
onCrossFadeTimerUpdate();
|
||||
}
|
||||
},
|
||||
true);
|
||||
|
||||
g_pHyprlock->renderOutput(outputPort);
|
||||
}
|
||||
} else if (!pendingResourceID.empty()) {
|
||||
Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
|
||||
g_pHyprlock->addTimer(std::chrono::milliseconds(100), [REF = m_self](auto, auto) { onAssetCallback(REF); }, nullptr);
|
||||
}
|
||||
|
||||
g_pHyprlock->renderOutput(outputPort);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "IWidget.hpp"
|
||||
#include "../../helpers/AnimatedVariable.hpp"
|
||||
#include "../../helpers/Color.hpp"
|
||||
#include "../../helpers/Math.hpp"
|
||||
#include "../../core/Timer.hpp"
|
||||
|
|
@ -16,15 +17,9 @@
|
|||
struct SPreloadedAsset;
|
||||
class COutput;
|
||||
|
||||
struct SFade {
|
||||
std::chrono::system_clock::time_point start;
|
||||
float a = 0;
|
||||
std::shared_ptr<CTimer> crossFadeTimer = nullptr;
|
||||
};
|
||||
|
||||
class CBackground : public IWidget {
|
||||
public:
|
||||
CBackground() = default;
|
||||
CBackground();
|
||||
~CBackground();
|
||||
|
||||
void registerSelf(const SP<CBackground>& self);
|
||||
|
|
@ -36,16 +31,18 @@ class CBackground : public IWidget {
|
|||
|
||||
void renderRect(CHyprColor color);
|
||||
|
||||
void renderBlur(const CTexture& text, CFramebuffer& fb);
|
||||
|
||||
void onReloadTimerUpdate();
|
||||
void onCrossFadeTimerUpdate();
|
||||
void plantReloadTimer();
|
||||
void startCrossFadeOrUpdateRender();
|
||||
void startCrossFade();
|
||||
|
||||
private:
|
||||
WP<CBackground> m_self;
|
||||
|
||||
// if needed
|
||||
CFramebuffer blurredFB;
|
||||
UP<CFramebuffer> blurredFB;
|
||||
UP<CFramebuffer> pendingBlurredFB;
|
||||
|
||||
int blurSize = 10;
|
||||
int blurPasses = 3;
|
||||
|
|
@ -61,18 +58,18 @@ class CBackground : public IWidget {
|
|||
Hyprutils::Math::eTransform transform;
|
||||
|
||||
std::string resourceID;
|
||||
std::string scResourceID;
|
||||
std::string pendingResourceID;
|
||||
|
||||
float crossFadeTime = -1.0;
|
||||
PHLANIMVAR<float> crossFadeProgress;
|
||||
|
||||
CHyprColor color;
|
||||
SPreloadedAsset* asset = nullptr;
|
||||
bool isScreenshot = false;
|
||||
SPreloadedAsset* scAsset = nullptr;
|
||||
SPreloadedAsset* pendingAsset = nullptr;
|
||||
bool isScreenshot = false;
|
||||
bool firstRender = true;
|
||||
|
||||
UP<SFade> fade;
|
||||
|
||||
int reloadTime = -1;
|
||||
std::string reloadCommand;
|
||||
CAsyncResourceGatherer::SPreloadRequest request;
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ void CImage::reset() {
|
|||
if (g_pHyprlock->m_bTerminate)
|
||||
return;
|
||||
|
||||
imageFB.release();
|
||||
imageFB.destroyBuffer();
|
||||
|
||||
if (asset && reloadTime > -1) // Don't unload asset if it's a static image
|
||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
||||
|
|
@ -217,7 +217,7 @@ void CImage::renderUpdate() {
|
|||
g_pRenderer->asyncResourceGatherer->unloadAsset(newAsset);
|
||||
} else if (resourceID != pendingResourceID) {
|
||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
||||
imageFB.release();
|
||||
imageFB.destroyBuffer();
|
||||
|
||||
asset = newAsset;
|
||||
resourceID = pendingResourceID;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue