mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-12-22 05:19:48 +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_out", 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.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <fstream>
|
||||
#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());
|
||||
if (!m_sWaylandState.display) {
|
||||
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");
|
||||
|
||||
if (!immediate) {
|
||||
const auto GRACE = (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);
|
||||
const auto PGRACE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:grace");
|
||||
m_tGraceEnds = **PGRACE ? std::chrono::system_clock::now() + std::chrono::seconds(**PGRACE) : std::chrono::system_clock::from_time_t(0);
|
||||
} else {
|
||||
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() {
|
||||
|
@ -383,7 +386,7 @@ void CHyprlock::run() {
|
|||
|
||||
// Hyprland violates the protocol a bit to allow for this.
|
||||
if (SZCURRENTD != "Hyprland") {
|
||||
while (!g_pRenderer->asyncResourceGatherer->ready) {
|
||||
while (!g_pRenderer->asyncResourceGatherer->gathered) {
|
||||
wl_display_flush(m_sWaylandState.display);
|
||||
if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
|
||||
wl_display_read_events(m_sWaylandState.display);
|
||||
|
@ -1137,5 +1140,5 @@ void CHyprlock::attemptRestoreOnDeath() {
|
|||
ofs.close();
|
||||
|
||||
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 {
|
||||
public:
|
||||
CHyprlock(const std::string& wlDisplay, const bool immediate);
|
||||
CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender);
|
||||
~CHyprlock();
|
||||
|
||||
void run();
|
||||
|
@ -100,6 +100,8 @@ class CHyprlock {
|
|||
bool m_bNumLock = false;
|
||||
bool m_bCtrl = false;
|
||||
bool m_bFadeStarted = false;
|
||||
|
||||
bool m_bImmediateRender = false;
|
||||
//
|
||||
std::chrono::system_clock::time_point m_tGraceEnds;
|
||||
std::chrono::system_clock::time_point m_tFadeEnds;
|
||||
|
|
11
src/main.cpp
11
src/main.cpp
|
@ -11,6 +11,7 @@ void help() {
|
|||
" -c FILE, --config FILE - Specify config file to use\n"
|
||||
" --display (display) - Specify the Wayland display to connect to\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";
|
||||
}
|
||||
|
||||
|
@ -26,8 +27,9 @@ std::optional<std::string> parseArg(const std::vector<std::string>& args, const
|
|||
int main(int argc, char** argv, char** envp) {
|
||||
std::string configPath;
|
||||
std::string wlDisplay;
|
||||
bool immediate = false;
|
||||
bool showHelp = false;
|
||||
bool immediate = false;
|
||||
bool immediateRender = false;
|
||||
bool showHelp = false;
|
||||
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
|
||||
|
@ -55,6 +57,9 @@ int main(int argc, char** argv, char** envp) {
|
|||
} else if (arg == "--immediate")
|
||||
immediate = true;
|
||||
|
||||
else if (arg == "--immediate-render")
|
||||
immediateRender = true;
|
||||
|
||||
else if (arg == "--help" || arg == "-h") {
|
||||
showHelp = true;
|
||||
break;
|
||||
|
@ -83,7 +88,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
}
|
||||
|
||||
try {
|
||||
g_pHyprlock = std::make_unique<CHyprlock>(wlDisplay, immediate);
|
||||
g_pHyprlock = std::make_unique<CHyprlock>(wlDisplay, immediate, immediateRender);
|
||||
g_pHyprlock->run();
|
||||
} catch (const std::exception& ex) {
|
||||
Debug::log(CRIT, "Hyprlock threw: {}", ex.what());
|
||||
|
|
|
@ -49,10 +49,10 @@ CAsyncResourceGatherer::CAsyncResourceGatherer() {
|
|||
dmas.emplace_back(std::make_unique<CDMAFrame>(PMONITOR));
|
||||
}
|
||||
|
||||
asyncLoopThread = std::thread([this]() {
|
||||
this->gather(); /* inital gather */
|
||||
this->asyncAssetSpinLock();
|
||||
});
|
||||
initialGatherThread = std::thread([this]() { this->gather(); });
|
||||
initialGatherThread.detach();
|
||||
|
||||
asyncLoopThread = std::thread([this]() { this->asyncAssetSpinLock(); });
|
||||
asyncLoopThread.detach();
|
||||
}
|
||||
|
||||
|
@ -158,16 +158,19 @@ void CAsyncResourceGatherer::gather() {
|
|||
const auto CAIRO = cairo_create(CAIROISURFACE);
|
||||
cairo_scale(CAIRO, 1, 1);
|
||||
|
||||
const auto TARGET = &preloadTargets.emplace_back(CAsyncResourceGatherer::SPreloadTarget{});
|
||||
{
|
||||
std::lock_guard lg{preloadTargetsMutex};
|
||||
const auto TARGET = &preloadTargets.emplace_back(CAsyncResourceGatherer::SPreloadTarget{});
|
||||
|
||||
TARGET->size = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)};
|
||||
TARGET->type = TARGET_IMAGE;
|
||||
TARGET->id = id;
|
||||
TARGET->size = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)};
|
||||
TARGET->type = TARGET_IMAGE;
|
||||
TARGET->id = id;
|
||||
|
||||
const auto DATA = cairo_image_surface_get_data(CAIROISURFACE);
|
||||
TARGET->cairo = CAIRO;
|
||||
TARGET->cairosurface = CAIROISURFACE;
|
||||
TARGET->data = DATA;
|
||||
const auto DATA = cairo_image_surface_get_data(CAIROISURFACE);
|
||||
TARGET->cairo = CAIRO;
|
||||
TARGET->cairosurface = CAIROISURFACE;
|
||||
TARGET->data = DATA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,7 +178,7 @@ void CAsyncResourceGatherer::gather() {
|
|||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
ready = true;
|
||||
gathered = true;
|
||||
}
|
||||
|
||||
bool CAsyncResourceGatherer::apply() {
|
||||
|
@ -225,7 +228,6 @@ bool CAsyncResourceGatherer::apply() {
|
|||
}
|
||||
}
|
||||
|
||||
applied = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -414,4 +416,6 @@ void CAsyncResourceGatherer::notify() {
|
|||
void CAsyncResourceGatherer::await() {
|
||||
if (asyncLoopThread.joinable())
|
||||
asyncLoopThread.join();
|
||||
if (initialGatherThread.joinable())
|
||||
initialGatherThread.join();
|
||||
}
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
class CAsyncResourceGatherer {
|
||||
public:
|
||||
CAsyncResourceGatherer();
|
||||
std::atomic<bool> ready = false;
|
||||
std::atomic<bool> applied = false;
|
||||
std::atomic<bool> gathered = false;
|
||||
|
||||
std::atomic<float> progress = 0;
|
||||
|
||||
|
@ -52,6 +51,7 @@ class CAsyncResourceGatherer {
|
|||
|
||||
private:
|
||||
std::thread asyncLoopThread;
|
||||
std::thread initialGatherThread;
|
||||
|
||||
void asyncAssetSpinLock();
|
||||
void renderText(const SPreloadRequest& rq);
|
||||
|
|
|
@ -159,7 +159,8 @@ CRenderer::CRenderer() {
|
|||
asyncResourceGatherer = std::make_unique<CAsyncResourceGatherer>();
|
||||
}
|
||||
|
||||
static int frames = 0;
|
||||
static int frames = 0;
|
||||
static bool firstFullFrame = false;
|
||||
|
||||
//
|
||||
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);
|
||||
|
||||
SRenderFeedback feedback;
|
||||
float bga = 0.0;
|
||||
const bool WAITFORASSETS = !g_pHyprlock->m_bImmediateRender && !asyncResourceGatherer->gathered;
|
||||
|
||||
float bga = asyncResourceGatherer->applied ?
|
||||
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 (WAITFORASSETS) {
|
||||
|
||||
if (!asyncResourceGatherer->ready) {
|
||||
// render status
|
||||
if (!**PDISABLEBAR) {
|
||||
CBox progress = {0, 0, asyncResourceGatherer->progress * surf.size.x, 2};
|
||||
|
@ -196,11 +196,13 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf
|
|||
}
|
||||
} else {
|
||||
|
||||
if (!asyncResourceGatherer->applied) {
|
||||
asyncResourceGatherer->apply();
|
||||
gatheredAt = std::chrono::system_clock::now();
|
||||
if (!firstFullFrame) {
|
||||
firstFullFrameTime = 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)
|
||||
bga = 1.0;
|
||||
|
||||
|
@ -220,7 +222,7 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf
|
|||
|
||||
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);
|
||||
|
||||
|
@ -325,7 +327,7 @@ std::vector<std::unique_ptr<IWidget>>* CRenderer::getOrCreateWidgetsFor(const CS
|
|||
resourceID = CDMAFrame::getResourceId(surf->output);
|
||||
// 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 (asyncResourceGatherer->ready) {
|
||||
if (asyncResourceGatherer->gathered) {
|
||||
if (!asyncResourceGatherer->getAssetByID(resourceID))
|
||||
resourceID = ""; // Fallback to solid color (background:color)
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ class CRenderer {
|
|||
void blurFB(const CFramebuffer& outfb, SBlurParams params);
|
||||
|
||||
std::unique_ptr<CAsyncResourceGatherer> asyncResourceGatherer;
|
||||
std::chrono::system_clock::time_point gatheredAt;
|
||||
std::chrono::system_clock::time_point firstFullFrameTime;
|
||||
|
||||
void pushFb(GLint fb);
|
||||
void popFb();
|
||||
|
|
|
@ -15,21 +15,29 @@ CBackground::CBackground(const Vector2D& viewport_, COutput* output_, const std:
|
|||
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) {
|
||||
|
||||
if (resourceID.empty()) {
|
||||
CBox monbox = {0, 0, viewport.x, viewport.y};
|
||||
CColor col = color;
|
||||
CColor col = color;
|
||||
col.a *= data.opacity;
|
||||
g_pRenderer->renderRect(monbox, col, 0);
|
||||
renderRect(col);
|
||||
return data.opacity < 1.0;
|
||||
}
|
||||
|
||||
if (!asset)
|
||||
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
|
||||
|
||||
if (!asset)
|
||||
if (!asset) {
|
||||
CColor col = color;
|
||||
col.a *= data.opacity;
|
||||
renderRect(col);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (asset->texture.m_iType == TEXTURE_INVALID) {
|
||||
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_);
|
||||
|
||||
virtual bool draw(const SRenderData& data);
|
||||
void renderRect(CColor color);
|
||||
|
||||
private:
|
||||
// if needed
|
||||
|
|
Loading…
Reference in a new issue