mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-11-16 23:05:58 +01:00
core: add option to render solid background immediatly when bg assets are not ready (#407)
* asyncResourceGatherer: start the asyncLoop at the same time as gather This is a prerequesit for labels beeing drawn, while backgrounds are note ready yet. * core: allow immediate rendering even when backgrounds are not gathered yet Note: We don't really need to call `asyncResourceGatherer::apply` in the `renderLock` function, since it will get called by a call to `asyncResourceGatherer::getAssetById` anyways. * background: render color rectangle when asset is not ready yet * config: add general:immediate_render config option * core: use the --immediate-render flag in attemptRestoreOnDeath
This commit is contained in:
parent
a50296c181
commit
0552a1eddd
10 changed files with 66 additions and 40 deletions
|
@ -55,6 +55,7 @@ void CConfigManager::init() {
|
||||||
m_config.addConfigValue("general:no_fade_in", Hyprlang::INT{0});
|
m_config.addConfigValue("general:no_fade_in", Hyprlang::INT{0});
|
||||||
m_config.addConfigValue("general:no_fade_out", Hyprlang::INT{0});
|
m_config.addConfigValue("general:no_fade_out", Hyprlang::INT{0});
|
||||||
m_config.addConfigValue("general:ignore_empty_input", Hyprlang::INT{0});
|
m_config.addConfigValue("general:ignore_empty_input", Hyprlang::INT{0});
|
||||||
|
m_config.addConfigValue("general:immediate_render", Hyprlang::INT{0});
|
||||||
m_config.addConfigValue("general:pam_module", Hyprlang::STRING{"hyprlock"});
|
m_config.addConfigValue("general:pam_module", Hyprlang::STRING{"hyprlock"});
|
||||||
|
|
||||||
m_config.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
m_config.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate) {
|
CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender) {
|
||||||
m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str());
|
m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str());
|
||||||
if (!m_sWaylandState.display) {
|
if (!m_sWaylandState.display) {
|
||||||
Debug::log(CRIT, "Couldn't connect to a wayland compositor");
|
Debug::log(CRIT, "Couldn't connect to a wayland compositor");
|
||||||
|
@ -32,11 +32,14 @@ CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate) {
|
||||||
Debug::log(ERR, "Failed to create xkb context");
|
Debug::log(ERR, "Failed to create xkb context");
|
||||||
|
|
||||||
if (!immediate) {
|
if (!immediate) {
|
||||||
const auto GRACE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:grace");
|
const auto PGRACE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:grace");
|
||||||
m_tGraceEnds = **GRACE ? std::chrono::system_clock::now() + std::chrono::seconds(**GRACE) : std::chrono::system_clock::from_time_t(0);
|
m_tGraceEnds = **PGRACE ? std::chrono::system_clock::now() + std::chrono::seconds(**PGRACE) : std::chrono::system_clock::from_time_t(0);
|
||||||
} else {
|
} else {
|
||||||
m_tGraceEnds = std::chrono::system_clock::from_time_t(0);
|
m_tGraceEnds = std::chrono::system_clock::from_time_t(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto PIMMEDIATERENDER = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:immediate_render");
|
||||||
|
m_bImmediateRender = immediateRender || **PIMMEDIATERENDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHyprlock::~CHyprlock() {
|
CHyprlock::~CHyprlock() {
|
||||||
|
@ -383,7 +386,7 @@ void CHyprlock::run() {
|
||||||
|
|
||||||
// Hyprland violates the protocol a bit to allow for this.
|
// Hyprland violates the protocol a bit to allow for this.
|
||||||
if (SZCURRENTD != "Hyprland") {
|
if (SZCURRENTD != "Hyprland") {
|
||||||
while (!g_pRenderer->asyncResourceGatherer->ready) {
|
while (!g_pRenderer->asyncResourceGatherer->gathered) {
|
||||||
wl_display_flush(m_sWaylandState.display);
|
wl_display_flush(m_sWaylandState.display);
|
||||||
if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
|
if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
|
||||||
wl_display_read_events(m_sWaylandState.display);
|
wl_display_read_events(m_sWaylandState.display);
|
||||||
|
@ -1137,5 +1140,5 @@ void CHyprlock::attemptRestoreOnDeath() {
|
||||||
ofs.close();
|
ofs.close();
|
||||||
|
|
||||||
spawnSync("hyprctl keyword misc:allow_session_lock_restore true");
|
spawnSync("hyprctl keyword misc:allow_session_lock_restore true");
|
||||||
spawnAsync("sleep 2 && hyprlock --immediate & disown");
|
spawnAsync("sleep 2 && hyprlock --immediate --immediate-render & disown");
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ struct SDMABUFModifier {
|
||||||
|
|
||||||
class CHyprlock {
|
class CHyprlock {
|
||||||
public:
|
public:
|
||||||
CHyprlock(const std::string& wlDisplay, const bool immediate);
|
CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender);
|
||||||
~CHyprlock();
|
~CHyprlock();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
@ -100,6 +100,8 @@ class CHyprlock {
|
||||||
bool m_bNumLock = false;
|
bool m_bNumLock = false;
|
||||||
bool m_bCtrl = false;
|
bool m_bCtrl = false;
|
||||||
bool m_bFadeStarted = false;
|
bool m_bFadeStarted = false;
|
||||||
|
|
||||||
|
bool m_bImmediateRender = false;
|
||||||
//
|
//
|
||||||
std::chrono::system_clock::time_point m_tGraceEnds;
|
std::chrono::system_clock::time_point m_tGraceEnds;
|
||||||
std::chrono::system_clock::time_point m_tFadeEnds;
|
std::chrono::system_clock::time_point m_tFadeEnds;
|
||||||
|
|
|
@ -11,6 +11,7 @@ void help() {
|
||||||
" -c FILE, --config FILE - Specify config file to use\n"
|
" -c FILE, --config FILE - Specify config file to use\n"
|
||||||
" --display (display) - Specify the Wayland display to connect to\n"
|
" --display (display) - Specify the Wayland display to connect to\n"
|
||||||
" --immediate - Lock immediately, ignoring any configured grace period\n"
|
" --immediate - Lock immediately, ignoring any configured grace period\n"
|
||||||
|
" --immediate-render - Do not wait for resources before drawing the background\n"
|
||||||
" -h, --help - Show this help message\n";
|
" -h, --help - Show this help message\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
std::string configPath;
|
std::string configPath;
|
||||||
std::string wlDisplay;
|
std::string wlDisplay;
|
||||||
bool immediate = false;
|
bool immediate = false;
|
||||||
|
bool immediateRender = false;
|
||||||
bool showHelp = false;
|
bool showHelp = false;
|
||||||
|
|
||||||
std::vector<std::string> args(argv, argv + argc);
|
std::vector<std::string> args(argv, argv + argc);
|
||||||
|
@ -55,6 +57,9 @@ int main(int argc, char** argv, char** envp) {
|
||||||
} else if (arg == "--immediate")
|
} else if (arg == "--immediate")
|
||||||
immediate = true;
|
immediate = true;
|
||||||
|
|
||||||
|
else if (arg == "--immediate-render")
|
||||||
|
immediateRender = true;
|
||||||
|
|
||||||
else if (arg == "--help" || arg == "-h") {
|
else if (arg == "--help" || arg == "-h") {
|
||||||
showHelp = true;
|
showHelp = true;
|
||||||
break;
|
break;
|
||||||
|
@ -83,7 +88,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
g_pHyprlock = std::make_unique<CHyprlock>(wlDisplay, immediate);
|
g_pHyprlock = std::make_unique<CHyprlock>(wlDisplay, immediate, immediateRender);
|
||||||
g_pHyprlock->run();
|
g_pHyprlock->run();
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
Debug::log(CRIT, "Hyprlock threw: {}", ex.what());
|
Debug::log(CRIT, "Hyprlock threw: {}", ex.what());
|
||||||
|
|
|
@ -49,10 +49,10 @@ CAsyncResourceGatherer::CAsyncResourceGatherer() {
|
||||||
dmas.emplace_back(std::make_unique<CDMAFrame>(PMONITOR));
|
dmas.emplace_back(std::make_unique<CDMAFrame>(PMONITOR));
|
||||||
}
|
}
|
||||||
|
|
||||||
asyncLoopThread = std::thread([this]() {
|
initialGatherThread = std::thread([this]() { this->gather(); });
|
||||||
this->gather(); /* inital gather */
|
initialGatherThread.detach();
|
||||||
this->asyncAssetSpinLock();
|
|
||||||
});
|
asyncLoopThread = std::thread([this]() { this->asyncAssetSpinLock(); });
|
||||||
asyncLoopThread.detach();
|
asyncLoopThread.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,6 +158,8 @@ void CAsyncResourceGatherer::gather() {
|
||||||
const auto CAIRO = cairo_create(CAIROISURFACE);
|
const auto CAIRO = cairo_create(CAIROISURFACE);
|
||||||
cairo_scale(CAIRO, 1, 1);
|
cairo_scale(CAIRO, 1, 1);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lg{preloadTargetsMutex};
|
||||||
const auto TARGET = &preloadTargets.emplace_back(CAsyncResourceGatherer::SPreloadTarget{});
|
const auto TARGET = &preloadTargets.emplace_back(CAsyncResourceGatherer::SPreloadTarget{});
|
||||||
|
|
||||||
TARGET->size = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)};
|
TARGET->size = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)};
|
||||||
|
@ -170,12 +172,13 @@ void CAsyncResourceGatherer::gather() {
|
||||||
TARGET->data = DATA;
|
TARGET->data = DATA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (std::any_of(dmas.begin(), dmas.end(), [](const auto& d) { return !d->asset.ready; })) {
|
while (std::any_of(dmas.begin(), dmas.end(), [](const auto& d) { return !d->asset.ready; })) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
ready = true;
|
gathered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CAsyncResourceGatherer::apply() {
|
bool CAsyncResourceGatherer::apply() {
|
||||||
|
@ -225,7 +228,6 @@ bool CAsyncResourceGatherer::apply() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applied = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,4 +416,6 @@ void CAsyncResourceGatherer::notify() {
|
||||||
void CAsyncResourceGatherer::await() {
|
void CAsyncResourceGatherer::await() {
|
||||||
if (asyncLoopThread.joinable())
|
if (asyncLoopThread.joinable())
|
||||||
asyncLoopThread.join();
|
asyncLoopThread.join();
|
||||||
|
if (initialGatherThread.joinable())
|
||||||
|
initialGatherThread.join();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
class CAsyncResourceGatherer {
|
class CAsyncResourceGatherer {
|
||||||
public:
|
public:
|
||||||
CAsyncResourceGatherer();
|
CAsyncResourceGatherer();
|
||||||
std::atomic<bool> ready = false;
|
std::atomic<bool> gathered = false;
|
||||||
std::atomic<bool> applied = false;
|
|
||||||
|
|
||||||
std::atomic<float> progress = 0;
|
std::atomic<float> progress = 0;
|
||||||
|
|
||||||
|
@ -52,6 +51,7 @@ class CAsyncResourceGatherer {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::thread asyncLoopThread;
|
std::thread asyncLoopThread;
|
||||||
|
std::thread initialGatherThread;
|
||||||
|
|
||||||
void asyncAssetSpinLock();
|
void asyncAssetSpinLock();
|
||||||
void renderText(const SPreloadRequest& rq);
|
void renderText(const SPreloadRequest& rq);
|
||||||
|
|
|
@ -160,6 +160,7 @@ CRenderer::CRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int frames = 0;
|
static int frames = 0;
|
||||||
|
static bool firstFullFrame = false;
|
||||||
|
|
||||||
//
|
//
|
||||||
CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf) {
|
CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf) {
|
||||||
|
@ -183,12 +184,11 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf
|
||||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
SRenderFeedback feedback;
|
SRenderFeedback feedback;
|
||||||
|
float bga = 0.0;
|
||||||
|
const bool WAITFORASSETS = !g_pHyprlock->m_bImmediateRender && !asyncResourceGatherer->gathered;
|
||||||
|
|
||||||
float bga = asyncResourceGatherer->applied ?
|
if (WAITFORASSETS) {
|
||||||
std::clamp(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - gatheredAt).count() / 500000.0, 0.0, 1.0) :
|
|
||||||
0.0;
|
|
||||||
|
|
||||||
if (!asyncResourceGatherer->ready) {
|
|
||||||
// render status
|
// render status
|
||||||
if (!**PDISABLEBAR) {
|
if (!**PDISABLEBAR) {
|
||||||
CBox progress = {0, 0, asyncResourceGatherer->progress * surf.size.x, 2};
|
CBox progress = {0, 0, asyncResourceGatherer->progress * surf.size.x, 2};
|
||||||
|
@ -196,11 +196,13 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (!asyncResourceGatherer->applied) {
|
if (!firstFullFrame) {
|
||||||
asyncResourceGatherer->apply();
|
firstFullFrameTime = std::chrono::system_clock::now();
|
||||||
gatheredAt = std::chrono::system_clock::now();
|
firstFullFrame = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bga = std::clamp(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - firstFullFrameTime).count() / 500000.0, 0.0, 1.0);
|
||||||
|
|
||||||
if (**PNOFADEIN)
|
if (**PNOFADEIN)
|
||||||
bga = 1.0;
|
bga = 1.0;
|
||||||
|
|
||||||
|
@ -220,7 +222,7 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf
|
||||||
|
|
||||||
Debug::log(TRACE, "frame {}", frames);
|
Debug::log(TRACE, "frame {}", frames);
|
||||||
|
|
||||||
feedback.needsFrame = feedback.needsFrame || !asyncResourceGatherer->ready || bga < 1.0;
|
feedback.needsFrame = feedback.needsFrame || !asyncResourceGatherer->gathered || bga < 1.0;
|
||||||
|
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
@ -325,7 +327,7 @@ std::vector<std::unique_ptr<IWidget>>* CRenderer::getOrCreateWidgetsFor(const CS
|
||||||
resourceID = CDMAFrame::getResourceId(surf->output);
|
resourceID = CDMAFrame::getResourceId(surf->output);
|
||||||
// When the initial gather of the asyncResourceGatherer is completed (ready), all DMAFrames are available.
|
// 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.
|
// Dynamic ones are tricky, because a screencopy would copy hyprlock itself.
|
||||||
if (asyncResourceGatherer->ready) {
|
if (asyncResourceGatherer->gathered) {
|
||||||
if (!asyncResourceGatherer->getAssetByID(resourceID))
|
if (!asyncResourceGatherer->getAssetByID(resourceID))
|
||||||
resourceID = ""; // Fallback to solid color (background:color)
|
resourceID = ""; // Fallback to solid color (background:color)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ class CRenderer {
|
||||||
void blurFB(const CFramebuffer& outfb, SBlurParams params);
|
void blurFB(const CFramebuffer& outfb, SBlurParams params);
|
||||||
|
|
||||||
std::unique_ptr<CAsyncResourceGatherer> asyncResourceGatherer;
|
std::unique_ptr<CAsyncResourceGatherer> asyncResourceGatherer;
|
||||||
std::chrono::system_clock::time_point gatheredAt;
|
std::chrono::system_clock::time_point firstFullFrameTime;
|
||||||
|
|
||||||
void pushFb(GLint fb);
|
void pushFb(GLint fb);
|
||||||
void popFb();
|
void popFb();
|
||||||
|
|
|
@ -15,21 +15,29 @@ CBackground::CBackground(const Vector2D& viewport_, COutput* output_, const std:
|
||||||
contrast = std::any_cast<Hyprlang::FLOAT>(props.at("contrast"));
|
contrast = std::any_cast<Hyprlang::FLOAT>(props.at("contrast"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CBackground::renderRect(CColor color) {
|
||||||
|
CBox monbox = {0, 0, viewport.x, viewport.y};
|
||||||
|
g_pRenderer->renderRect(monbox, color, 0);
|
||||||
|
}
|
||||||
|
|
||||||
bool CBackground::draw(const SRenderData& data) {
|
bool CBackground::draw(const SRenderData& data) {
|
||||||
|
|
||||||
if (resourceID.empty()) {
|
if (resourceID.empty()) {
|
||||||
CBox monbox = {0, 0, viewport.x, viewport.y};
|
|
||||||
CColor col = color;
|
CColor col = color;
|
||||||
col.a *= data.opacity;
|
col.a *= data.opacity;
|
||||||
g_pRenderer->renderRect(monbox, col, 0);
|
renderRect(col);
|
||||||
return data.opacity < 1.0;
|
return data.opacity < 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!asset)
|
if (!asset)
|
||||||
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
|
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
|
||||||
|
|
||||||
if (!asset)
|
if (!asset) {
|
||||||
|
CColor col = color;
|
||||||
|
col.a *= data.opacity;
|
||||||
|
renderRect(col);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (asset->texture.m_iType == TEXTURE_INVALID) {
|
if (asset->texture.m_iType == TEXTURE_INVALID) {
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
||||||
|
|
|
@ -16,6 +16,7 @@ class CBackground : public IWidget {
|
||||||
CBackground(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map<std::string, std::any>& props, bool ss_);
|
CBackground(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map<std::string, std::any>& props, bool ss_);
|
||||||
|
|
||||||
virtual bool draw(const SRenderData& data);
|
virtual bool draw(const SRenderData& data);
|
||||||
|
void renderRect(CColor color);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// if needed
|
// if needed
|
||||||
|
|
Loading…
Reference in a new issue