mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-11-16 23:05:58 +01:00
image: add reload options (#247)
* image: add reload options * check for actual file changes * use modtime * check only same paths * add Nix HM
This commit is contained in:
parent
071ebcefb9
commit
bbbb960e42
6 changed files with 180 additions and 15 deletions
|
@ -225,6 +225,18 @@ in {
|
||||||
default = 0.0;
|
default = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reload_time = mkOption {
|
||||||
|
description = "Interval in seconds between reloading the image";
|
||||||
|
type = int;
|
||||||
|
default = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
reload_cmd = mkOption {
|
||||||
|
description = "Command to obtain new path";
|
||||||
|
type = str;
|
||||||
|
default = "";
|
||||||
|
};
|
||||||
|
|
||||||
position = {
|
position = {
|
||||||
x = mkOption {
|
x = mkOption {
|
||||||
description = "X position of the image";
|
description = "X position of the image";
|
||||||
|
@ -558,6 +570,8 @@ in {
|
||||||
border_size = ${toString image.border_size}
|
border_size = ${toString image.border_size}
|
||||||
border_color = ${image.border_color}
|
border_color = ${image.border_color}
|
||||||
rotate = ${toString image.rotate}
|
rotate = ${toString image.rotate}
|
||||||
|
reload_time = ${toString image.reload_time}
|
||||||
|
reload_cmd = ${image.reload_cmd}
|
||||||
|
|
||||||
position = ${toString image.position.x}, ${toString image.position.y}
|
position = ${toString image.position.x}, ${toString image.position.y}
|
||||||
halign = ${image.halign}
|
halign = ${image.halign}
|
||||||
|
|
|
@ -73,6 +73,8 @@ void CConfigManager::init() {
|
||||||
m_config.addSpecialConfigValue("image", "halign", Hyprlang::STRING{"center"});
|
m_config.addSpecialConfigValue("image", "halign", Hyprlang::STRING{"center"});
|
||||||
m_config.addSpecialConfigValue("image", "valign", Hyprlang::STRING{"center"});
|
m_config.addSpecialConfigValue("image", "valign", Hyprlang::STRING{"center"});
|
||||||
m_config.addSpecialConfigValue("image", "rotate", Hyprlang::FLOAT{0});
|
m_config.addSpecialConfigValue("image", "rotate", Hyprlang::FLOAT{0});
|
||||||
|
m_config.addSpecialConfigValue("image", "reload_time", Hyprlang::INT{-1});
|
||||||
|
m_config.addSpecialConfigValue("image", "reload_cmd", Hyprlang::STRING{""});
|
||||||
SHADOWABLE("image");
|
SHADOWABLE("image");
|
||||||
|
|
||||||
m_config.addSpecialCategory("input-field", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
m_config.addSpecialCategory("input-field", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||||
|
@ -184,6 +186,8 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
|
||||||
{"halign", m_config.getSpecialConfigValue("image", "halign", k.c_str())},
|
{"halign", m_config.getSpecialConfigValue("image", "halign", k.c_str())},
|
||||||
{"valign", m_config.getSpecialConfigValue("image", "valign", k.c_str())},
|
{"valign", m_config.getSpecialConfigValue("image", "valign", k.c_str())},
|
||||||
{"rotate", m_config.getSpecialConfigValue("image", "rotate", k.c_str())},
|
{"rotate", m_config.getSpecialConfigValue("image", "rotate", k.c_str())},
|
||||||
|
{"reload_time", m_config.getSpecialConfigValue("image", "reload_time", k.c_str())},
|
||||||
|
{"reload_cmd", m_config.getSpecialConfigValue("image", "reload_cmd", k.c_str())},
|
||||||
SHADOWABLE("image"),
|
SHADOWABLE("image"),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -203,6 +203,25 @@ void CAsyncResourceGatherer::apply() {
|
||||||
applied = true;
|
applied = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CAsyncResourceGatherer::renderImage(const SPreloadRequest& rq) {
|
||||||
|
SPreloadTarget target;
|
||||||
|
target.type = TARGET_IMAGE;
|
||||||
|
target.id = rq.id;
|
||||||
|
|
||||||
|
const auto ABSOLUTEPATH = absolutePath(rq.asset, "");
|
||||||
|
const auto CAIROISURFACE = cairo_image_surface_create_from_png(ABSOLUTEPATH.c_str());
|
||||||
|
|
||||||
|
const auto CAIRO = cairo_create(CAIROISURFACE);
|
||||||
|
cairo_scale(CAIRO, 1, 1);
|
||||||
|
|
||||||
|
target.cairo = CAIRO;
|
||||||
|
target.cairosurface = CAIROISURFACE;
|
||||||
|
target.data = cairo_image_surface_get_data(CAIROISURFACE);
|
||||||
|
target.size = {(double)cairo_image_surface_get_width(CAIROISURFACE), (double)cairo_image_surface_get_height(CAIROISURFACE)};
|
||||||
|
|
||||||
|
preloadTargets.push_back(target);
|
||||||
|
}
|
||||||
|
|
||||||
void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
|
void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
|
||||||
SPreloadTarget target;
|
SPreloadTarget target;
|
||||||
target.type = TARGET_IMAGE; /* text is just an image lol */
|
target.type = TARGET_IMAGE; /* text is just an image lol */
|
||||||
|
@ -314,6 +333,8 @@ void CAsyncResourceGatherer::asyncAssetSpinLock() {
|
||||||
for (auto& r : requests) {
|
for (auto& r : requests) {
|
||||||
if (r.type == TARGET_TEXT) {
|
if (r.type == TARGET_TEXT) {
|
||||||
renderText(r);
|
renderText(r);
|
||||||
|
} else if (r.type == TARGET_IMAGE) {
|
||||||
|
renderImage(r);
|
||||||
} else {
|
} else {
|
||||||
Debug::log(ERR, "Unsupported async preload type {}??", (int)r.type);
|
Debug::log(ERR, "Unsupported async preload type {}??", (int)r.type);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -56,6 +56,7 @@ class CAsyncResourceGatherer {
|
||||||
|
|
||||||
void asyncAssetSpinLock();
|
void asyncAssetSpinLock();
|
||||||
void renderText(const SPreloadRequest& rq);
|
void renderText(const SPreloadRequest& rq);
|
||||||
|
void renderImage(const SPreloadRequest& rq);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::condition_variable loopGuard;
|
std::condition_variable loopGuard;
|
||||||
|
|
|
@ -1,7 +1,76 @@
|
||||||
#include "Image.hpp"
|
#include "Image.hpp"
|
||||||
#include "../Renderer.hpp"
|
#include "../Renderer.hpp"
|
||||||
|
#include "../../core/hyprlock.hpp"
|
||||||
|
#include "../../helpers/Log.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
CImage::~CImage() {
|
||||||
|
imageTimer->cancel();
|
||||||
|
imageTimer.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onTimer(std::shared_ptr<CTimer> self, void* data) {
|
||||||
|
const auto PIMAGE = (CImage*)data;
|
||||||
|
|
||||||
|
PIMAGE->onTimerUpdate();
|
||||||
|
PIMAGE->plantTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onAssetCallback(void* data) {
|
||||||
|
const auto PIMAGE = (CImage*)data;
|
||||||
|
PIMAGE->renderSuper();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CImage::onTimerUpdate() {
|
||||||
|
const std::string OLDPATH = path;
|
||||||
|
|
||||||
|
if (!reloadCommand.empty()) {
|
||||||
|
path = g_pHyprlock->spawnSync(reloadCommand);
|
||||||
|
|
||||||
|
if (path.ends_with('\n'))
|
||||||
|
path.pop_back();
|
||||||
|
|
||||||
|
if (path.starts_with("file://"))
|
||||||
|
path = path.substr(7);
|
||||||
|
|
||||||
|
if (path.empty())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const auto MTIME = std::filesystem::last_write_time(path);
|
||||||
|
if (OLDPATH == path && MTIME == modificationTime)
|
||||||
|
return;
|
||||||
|
|
||||||
|
modificationTime = MTIME;
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
path = OLDPATH;
|
||||||
|
Debug::log(ERR, "{}", e.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pendingResourceID.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
request.id = std::string{"image:"} + path + ",time:" + std::to_string(modificationTime.time_since_epoch().count());
|
||||||
|
pendingResourceID = request.id;
|
||||||
|
request.asset = path;
|
||||||
|
request.type = CAsyncResourceGatherer::eTargetType::TARGET_IMAGE;
|
||||||
|
|
||||||
|
request.callback = onAssetCallback;
|
||||||
|
request.callbackData = this;
|
||||||
|
|
||||||
|
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CImage::plantTimer() {
|
||||||
|
|
||||||
|
if (reloadTime == 0) {
|
||||||
|
imageTimer = g_pHyprlock->addTimer(std::chrono::hours(1), onTimer, this, true);
|
||||||
|
} else if (reloadTime > 0)
|
||||||
|
imageTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), onTimer, this, false);
|
||||||
|
}
|
||||||
|
|
||||||
CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& resourceID_, const std::unordered_map<std::string, std::any>& props) :
|
CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& resourceID_, const std::unordered_map<std::string, std::any>& props) :
|
||||||
viewport(viewport_), resourceID(resourceID_), output(output_), shadow(this, props, viewport_) {
|
viewport(viewport_), resourceID(resourceID_), output(output_), shadow(this, props, viewport_) {
|
||||||
|
|
||||||
|
@ -14,11 +83,38 @@ CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& r
|
||||||
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
|
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
|
||||||
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
|
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
|
||||||
|
|
||||||
|
path = std::any_cast<Hyprlang::STRING>(props.at("path"));
|
||||||
|
reloadTime = std::any_cast<Hyprlang::INT>(props.at("reload_time"));
|
||||||
|
reloadCommand = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
modificationTime = std::filesystem::last_write_time(path);
|
||||||
|
} catch (std::exception& e) { Debug::log(ERR, "{}", e.what()); }
|
||||||
|
|
||||||
angle = angle * M_PI / 180.0;
|
angle = angle * M_PI / 180.0;
|
||||||
|
|
||||||
|
plantTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CImage::draw(const SRenderData& data) {
|
bool CImage::draw(const SRenderData& data) {
|
||||||
|
|
||||||
|
if (!pendingResourceID.empty()) {
|
||||||
|
auto newAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(pendingResourceID);
|
||||||
|
if (newAsset) {
|
||||||
|
if (newAsset->texture.m_iType == TEXTURE_INVALID) {
|
||||||
|
g_pRenderer->asyncResourceGatherer->unloadAsset(newAsset);
|
||||||
|
} else if (resourceID != pendingResourceID) {
|
||||||
|
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
||||||
|
imageFB.release();
|
||||||
|
|
||||||
|
asset = newAsset;
|
||||||
|
resourceID = pendingResourceID;
|
||||||
|
firstRender = true;
|
||||||
|
}
|
||||||
|
pendingResourceID = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (resourceID.empty())
|
if (resourceID.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -54,6 +150,8 @@ bool CImage::draw(const SRenderData& data) {
|
||||||
borderBox.round();
|
borderBox.round();
|
||||||
imageFB.alloc(borderBox.w, borderBox.h, true);
|
imageFB.alloc(borderBox.w, borderBox.h, true);
|
||||||
g_pRenderer->pushFb(imageFB.m_iFb);
|
g_pRenderer->pushFb(imageFB.m_iFb);
|
||||||
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
if (border > 0)
|
if (border > 0)
|
||||||
g_pRenderer->renderRect(borderBox, color, ALLOWROUND ? rounding : std::min(borderBox.w, borderBox.h) / 2.0);
|
g_pRenderer->renderRect(borderBox, color, ALLOWROUND ? rounding : std::min(borderBox.w, borderBox.h) / 2.0);
|
||||||
|
@ -84,3 +182,15 @@ bool CImage::draw(const SRenderData& data) {
|
||||||
|
|
||||||
return data.opacity < 1.0;
|
return data.opacity < 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void onAssetCallbackTimer(std::shared_ptr<CTimer> self, void* data) {
|
||||||
|
const auto PIMAGE = (CImage*)data;
|
||||||
|
PIMAGE->renderSuper();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CImage::renderSuper() {
|
||||||
|
g_pHyprlock->renderOutput(output->stringPort);
|
||||||
|
|
||||||
|
if (!pendingResourceID.empty()) /* did not consume the pending resource */
|
||||||
|
g_pHyprlock->addTimer(std::chrono::milliseconds(100), onAssetCallbackTimer, this);
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
#include "IWidget.hpp"
|
#include "IWidget.hpp"
|
||||||
#include "../../helpers/Vector2D.hpp"
|
#include "../../helpers/Vector2D.hpp"
|
||||||
#include "../../helpers/Color.hpp"
|
#include "../../helpers/Color.hpp"
|
||||||
|
#include "../../core/Timer.hpp"
|
||||||
|
#include "../AsyncResourceGatherer.hpp"
|
||||||
#include "Shadowable.hpp"
|
#include "Shadowable.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <any>
|
#include <any>
|
||||||
|
|
||||||
|
@ -14,9 +17,14 @@ class COutput;
|
||||||
class CImage : public IWidget {
|
class CImage : public IWidget {
|
||||||
public:
|
public:
|
||||||
CImage(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map<std::string, std::any>& props);
|
CImage(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map<std::string, std::any>& props);
|
||||||
|
~CImage();
|
||||||
|
|
||||||
virtual bool draw(const SRenderData& data);
|
virtual bool draw(const SRenderData& data);
|
||||||
|
|
||||||
|
void renderSuper();
|
||||||
|
void onTimerUpdate();
|
||||||
|
void plantTimer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CFramebuffer imageFB;
|
CFramebuffer imageFB;
|
||||||
|
|
||||||
|
@ -27,12 +35,19 @@ class CImage : public IWidget {
|
||||||
CColor color;
|
CColor color;
|
||||||
Vector2D pos;
|
Vector2D pos;
|
||||||
|
|
||||||
std::string halign, valign;
|
std::string halign, valign, path;
|
||||||
|
|
||||||
bool firstRender = true;
|
bool firstRender = true;
|
||||||
|
|
||||||
|
int reloadTime;
|
||||||
|
std::string reloadCommand;
|
||||||
|
std::filesystem::file_time_type modificationTime;
|
||||||
|
std::shared_ptr<CTimer> imageTimer;
|
||||||
|
CAsyncResourceGatherer::SPreloadRequest request;
|
||||||
|
|
||||||
Vector2D viewport;
|
Vector2D viewport;
|
||||||
std::string resourceID;
|
std::string resourceID;
|
||||||
|
std::string pendingResourceID; // if reloading image
|
||||||
SPreloadedAsset* asset = nullptr;
|
SPreloadedAsset* asset = nullptr;
|
||||||
COutput* output = nullptr;
|
COutput* output = nullptr;
|
||||||
CShadowable shadow;
|
CShadowable shadow;
|
||||||
|
|
Loading…
Reference in a new issue