mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-12-22 05:19:48 +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;
|
||||
};
|
||||
|
||||
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 = {
|
||||
x = mkOption {
|
||||
description = "X position of the image";
|
||||
|
@ -558,6 +570,8 @@ in {
|
|||
border_size = ${toString image.border_size}
|
||||
border_color = ${image.border_color}
|
||||
rotate = ${toString image.rotate}
|
||||
reload_time = ${toString image.reload_time}
|
||||
reload_cmd = ${image.reload_cmd}
|
||||
|
||||
position = ${toString image.position.x}, ${toString image.position.y}
|
||||
halign = ${image.halign}
|
||||
|
|
|
@ -73,6 +73,8 @@ void CConfigManager::init() {
|
|||
m_config.addSpecialConfigValue("image", "halign", Hyprlang::STRING{"center"});
|
||||
m_config.addSpecialConfigValue("image", "valign", Hyprlang::STRING{"center"});
|
||||
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");
|
||||
|
||||
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())},
|
||||
{"valign", m_config.getSpecialConfigValue("image", "valign", 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"),
|
||||
}
|
||||
});
|
||||
|
|
|
@ -203,6 +203,25 @@ void CAsyncResourceGatherer::apply() {
|
|||
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) {
|
||||
SPreloadTarget target;
|
||||
target.type = TARGET_IMAGE; /* text is just an image lol */
|
||||
|
@ -314,6 +333,8 @@ void CAsyncResourceGatherer::asyncAssetSpinLock() {
|
|||
for (auto& r : requests) {
|
||||
if (r.type == TARGET_TEXT) {
|
||||
renderText(r);
|
||||
} else if (r.type == TARGET_IMAGE) {
|
||||
renderImage(r);
|
||||
} else {
|
||||
Debug::log(ERR, "Unsupported async preload type {}??", (int)r.type);
|
||||
continue;
|
||||
|
|
|
@ -56,6 +56,7 @@ class CAsyncResourceGatherer {
|
|||
|
||||
void asyncAssetSpinLock();
|
||||
void renderText(const SPreloadRequest& rq);
|
||||
void renderImage(const SPreloadRequest& rq);
|
||||
|
||||
struct {
|
||||
std::condition_variable loopGuard;
|
||||
|
@ -88,4 +89,4 @@ class CAsyncResourceGatherer {
|
|||
std::unordered_map<std::string, SPreloadedAsset> assets;
|
||||
|
||||
void gather();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,7 +1,76 @@
|
|||
#include "Image.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
#include "../../core/hyprlock.hpp"
|
||||
#include "../../helpers/Log.hpp"
|
||||
#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) :
|
||||
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"));
|
||||
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;
|
||||
|
||||
plantTimer();
|
||||
}
|
||||
|
||||
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())
|
||||
return false;
|
||||
|
||||
|
@ -54,6 +150,8 @@ bool CImage::draw(const SRenderData& data) {
|
|||
borderBox.round();
|
||||
imageFB.alloc(borderBox.w, borderBox.h, true);
|
||||
g_pRenderer->pushFb(imageFB.m_iFb);
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (border > 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;
|
||||
}
|
||||
|
||||
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 "../../helpers/Vector2D.hpp"
|
||||
#include "../../helpers/Color.hpp"
|
||||
#include "../../core/Timer.hpp"
|
||||
#include "../AsyncResourceGatherer.hpp"
|
||||
#include "Shadowable.hpp"
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
#include <any>
|
||||
|
||||
|
@ -14,26 +17,38 @@ class COutput;
|
|||
class CImage : public IWidget {
|
||||
public:
|
||||
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);
|
||||
|
||||
void renderSuper();
|
||||
void onTimerUpdate();
|
||||
void plantTimer();
|
||||
|
||||
private:
|
||||
CFramebuffer imageFB;
|
||||
CFramebuffer imageFB;
|
||||
|
||||
int size;
|
||||
int rounding;
|
||||
double border;
|
||||
double angle;
|
||||
CColor color;
|
||||
Vector2D pos;
|
||||
int size;
|
||||
int rounding;
|
||||
double border;
|
||||
double angle;
|
||||
CColor color;
|
||||
Vector2D pos;
|
||||
|
||||
std::string halign, valign;
|
||||
std::string halign, valign, path;
|
||||
|
||||
bool firstRender = true;
|
||||
bool firstRender = true;
|
||||
|
||||
Vector2D viewport;
|
||||
std::string resourceID;
|
||||
SPreloadedAsset* asset = nullptr;
|
||||
COutput* output = nullptr;
|
||||
CShadowable shadow;
|
||||
int reloadTime;
|
||||
std::string reloadCommand;
|
||||
std::filesystem::file_time_type modificationTime;
|
||||
std::shared_ptr<CTimer> imageTimer;
|
||||
CAsyncResourceGatherer::SPreloadRequest request;
|
||||
|
||||
Vector2D viewport;
|
||||
std::string resourceID;
|
||||
std::string pendingResourceID; // if reloading image
|
||||
SPreloadedAsset* asset = nullptr;
|
||||
COutput* output = nullptr;
|
||||
CShadowable shadow;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue