label: add time and dynamic timers

This commit is contained in:
Vaxry 2024-02-20 00:11:19 +00:00
parent c60b7b8aef
commit 7370fc624f
9 changed files with 255 additions and 38 deletions

View file

@ -4,8 +4,12 @@
#include "../renderer/Renderer.hpp" #include "../renderer/Renderer.hpp"
#include "Password.hpp" #include "Password.hpp"
#include "Egl.hpp" #include "Egl.hpp"
#include <sys/wait.h>
#include <sys/poll.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <cuchar> #include <fcntl.h>
#include <unistd.h>
CHyprlock::CHyprlock(const std::string& wlDisplay) { CHyprlock::CHyprlock(const std::string& wlDisplay) {
m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str()); m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str());
@ -110,9 +114,120 @@ void CHyprlock::run() {
lockSession(); lockSession();
while (wl_display_dispatch(m_sWaylandState.display) != -1) { pollfd pollfds[] = {
{
.fd = wl_display_get_fd(m_sWaylandState.display),
.events = POLLIN,
},
};
std::thread pollThr([this, &pollfds]() {
while (1) {
int ret = poll(pollfds, 1, 5000 /* 5 seconds, reasonable. It's because we might need to terminate */);
if (ret < 0) {
Debug::log(CRIT, "[core] Polling fds failed with {}", errno);
m_bTerminate = true;
exit(1);
}
for (size_t i = 0; i < 3; ++i) {
if (pollfds[i].revents & POLLHUP) {
Debug::log(CRIT, "[core] Disconnected from pollfd id {}", i);
m_bTerminate = true;
exit(1);
}
}
if (m_bTerminate) if (m_bTerminate)
break; break;
if (ret != 0) {
Debug::log(TRACE, "[core] got poll event");
std::lock_guard<std::mutex> lg2(m_sLoopState.eventLoopMutex);
m_sLoopState.event = true;
m_sLoopState.loopCV.notify_all();
}
}
});
std::thread timersThr([this]() {
while (1) {
// calc nearest thing
m_sLoopState.timersMutex.lock();
float least = 10000;
for (auto& t : m_vTimers) {
const auto TIME = t->leftMs();
if (TIME < least)
least = TIME;
}
m_sLoopState.timersMutex.unlock();
std::unique_lock lk(m_sLoopState.timerRequestMutex);
m_sLoopState.timerCV.wait_for(lk, std::chrono::milliseconds((int)least + 1), [this] { return m_sLoopState.event; });
// notify main
std::lock_guard<std::mutex> lg2(m_sLoopState.eventLoopMutex);
Debug::log(TRACE, "timer thread firing");
m_sLoopState.event = true;
m_sLoopState.loopCV.notify_all();
}
});
m_sLoopState.event = true; // let it process once
while (1) {
std::unique_lock lk(m_sLoopState.eventRequestMutex);
if (m_sLoopState.event == false)
m_sLoopState.loopCV.wait(lk, [this] { return m_sLoopState.event; });
if (m_bTerminate)
break;
std::lock_guard<std::mutex> lg(m_sLoopState.eventLoopMutex);
m_sLoopState.event = false;
if (pollfds[0].revents & POLLIN /* dbus */) {
Debug::log(TRACE, "got wl event");
wl_display_flush(m_sWaylandState.display);
if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
wl_display_read_events(m_sWaylandState.display);
wl_display_dispatch_pending(m_sWaylandState.display);
} else {
wl_display_dispatch(m_sWaylandState.display);
}
}
m_sLoopState.timersMutex.lock();
auto timerscpy = m_vTimers;
m_sLoopState.timersMutex.unlock();
std::vector<std::shared_ptr<CTimer>> passed;
for (auto& t : timerscpy) {
if (t->passed() && !t->cancelled()) {
t->call(t);
passed.push_back(t);
}
if (t->cancelled())
passed.push_back(t);
}
m_sLoopState.timersMutex.lock();
std::erase_if(m_vTimers, [passed](const auto& timer) { return std::find(passed.begin(), passed.end(), timer) != passed.end(); });
m_sLoopState.timersMutex.unlock();
passed.clear();
// finalize wayland dispatching. Dispatch pending on the queue
int ret = 0;
do {
ret = wl_display_dispatch_pending(m_sWaylandState.display);
wl_display_flush(m_sWaylandState.display);
} while (ret > 0);
} }
Debug::log(LOG, "Reached the end, exiting"); Debug::log(LOG, "Reached the end, exiting");
@ -375,3 +490,8 @@ wp_viewporter* CHyprlock::getViewporter() {
size_t CHyprlock::getPasswordBufferLen() { size_t CHyprlock::getPasswordBufferLen() {
return m_sPasswordState.passBuffer.length(); return m_sPasswordState.passBuffer.length();
} }
std::shared_ptr<CTimer> CHyprlock::addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(std::shared_ptr<CTimer> self, void* data)> cb_, void* data) {
std::lock_guard<std::mutex> lg(m_sLoopState.timersMutex);
return m_vTimers.emplace_back(std::make_shared<CTimer>(timeout, cb_, data));
}

View file

@ -6,9 +6,11 @@
#include "viewporter-protocol.h" #include "viewporter-protocol.h"
#include "Output.hpp" #include "Output.hpp"
#include "CursorShape.hpp" #include "CursorShape.hpp"
#include "Timer.hpp"
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <condition_variable>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
@ -21,6 +23,8 @@ class CHyprlock {
void onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version); void onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version);
void onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name); void onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name);
std::shared_ptr<CTimer> addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(std::shared_ptr<CTimer> self, void* data)> cb_, void* data);
void onLockLocked(); void onLockLocked();
void onLockFinished(); void onLockFinished();
@ -68,7 +72,20 @@ class CHyprlock {
std::string passBuffer = ""; std::string passBuffer = "";
} m_sPasswordState; } m_sPasswordState;
struct {
std::mutex timersMutex;
std::mutex eventRequestMutex;
std::mutex eventLoopMutex;
std::condition_variable loopCV;
bool event = false;
std::condition_variable timerCV;
std::mutex timerRequestMutex;
} m_sLoopState;
std::vector<std::unique_ptr<COutput>> m_vOutputs; std::vector<std::unique_ptr<COutput>> m_vOutputs;
std::vector<std::shared_ptr<CTimer>> m_vTimers;
}; };
inline std::unique_ptr<CHyprlock> g_pHyprlock; inline std::unique_ptr<CHyprlock> g_pHyprlock;

View file

@ -14,8 +14,10 @@ int main(int argc, char** argv, char** envp) {
else if (arg == "--quiet" || arg == "-q") else if (arg == "--quiet" || arg == "-q")
Debug::quiet = true; Debug::quiet = true;
else if (arg == "--display" && i + 1 < argc) else if (arg == "--display" && i + 1 < argc) {
wlDisplay = argv[i + 1]; wlDisplay = argv[i + 1];
i++;
}
} }
try { try {

View file

@ -89,6 +89,7 @@ void CAsyncResourceGatherer::apply() {
for (auto& t : preloadTargets) { for (auto& t : preloadTargets) {
if (t.type == TARGET_IMAGE) { if (t.type == TARGET_IMAGE) {
std::lock_guard<std::mutex> lg(asyncLoopState.assetsMutex);
const auto ASSET = &assets[t.id]; const auto ASSET = &assets[t.id];
const auto CAIROFORMAT = cairo_image_surface_get_format((cairo_surface_t*)t.cairosurface); const auto CAIROFORMAT = cairo_image_surface_get_format((cairo_surface_t*)t.cairosurface);
@ -234,3 +235,9 @@ void CAsyncResourceGatherer::requestAsyncAssetPreload(const SPreloadRequest& req
asyncLoopState.pending = true; asyncLoopState.pending = true;
asyncLoopState.loopGuard.notify_all(); asyncLoopState.loopGuard.notify_all();
} }
void CAsyncResourceGatherer::unloadAsset(SPreloadedAsset* asset) {
std::lock_guard<std::mutex> lg(asyncLoopState.assetsMutex);
std::erase_if(assets, [asset](const auto& a) { return &a.second == asset; });
}

View file

@ -42,6 +42,7 @@ class CAsyncResourceGatherer {
}; };
void requestAsyncAssetPreload(const SPreloadRequest& request); void requestAsyncAssetPreload(const SPreloadRequest& request);
void unloadAsset(SPreloadedAsset* asset);
private: private:
std::thread initThread; std::thread initThread;
@ -56,6 +57,8 @@ class CAsyncResourceGatherer {
std::mutex requestMutex; std::mutex requestMutex;
std::mutex assetsMutex;
std::vector<SPreloadRequest> requests; std::vector<SPreloadRequest> requests;
bool pending = false; bool pending = false;

View file

@ -258,7 +258,7 @@ std::vector<std::unique_ptr<IWidget>>* CRenderer::getOrCreateWidgetsFor(const CS
} else if (c.type == "input-field") { } else if (c.type == "input-field") {
widgets[surf].emplace_back(std::make_unique<CPasswordInputField>(surf->size, c.values)); widgets[surf].emplace_back(std::make_unique<CPasswordInputField>(surf->size, c.values));
} else if (c.type == "label") { } else if (c.type == "label") {
widgets[surf].emplace_back(std::make_unique<CLabel>(surf->size, c.values)); widgets[surf].emplace_back(std::make_unique<CLabel>(surf->size, c.values, /* evil */ const_cast<CSessionLockSurface*>(surf)));
} }
} }
} }

View file

@ -12,4 +12,11 @@ class IWidget {
virtual bool draw(const SRenderData& data) = 0; virtual bool draw(const SRenderData& data) = 0;
virtual Vector2D posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign); virtual Vector2D posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign);
struct SFormatResult {
std::string formatted;
float updateEveryMs = 0; // 0 means don't (static)
};
virtual SFormatResult formatString(std::string in);
}; };

View file

@ -3,32 +3,59 @@
#include <hyprlang.hpp> #include <hyprlang.hpp>
#include "../Renderer.hpp" #include "../Renderer.hpp"
#include "../../helpers/Log.hpp" #include "../../helpers/Log.hpp"
#include "../../core/hyprlock.hpp"
void replaceAll(std::string& str, const std::string& from, const std::string& to) { CLabel::~CLabel() {
if (from.empty()) labelTimer->cancel();
labelTimer.reset();
}
static void onTimer(std::shared_ptr<CTimer> self, void* data) {
const auto PLABEL = (CLabel*)data;
// update label
PLABEL->onTimerUpdate();
// render and replant
PLABEL->renderSuper();
PLABEL->plantTimer();
}
void CLabel::onTimerUpdate() {
std::string oldFormatted = label.formatted;
label = formatString(labelPreFormat);
if (label.formatted == oldFormatted)
return; return;
size_t pos = 0;
while ((pos = str.find(from, pos)) != std::string::npos) { if (!pendingResourceID.empty())
str.replace(pos, from.length(), to); return; // too many updates, we'll miss some. Shouldn't happen tbh
pos += to.length();
} // request new
request.id = std::string{"label:"} + std::to_string((uintptr_t)this) + ",time:" + std::to_string(time(nullptr));
pendingResourceID = request.id;
request.asset = label.formatted;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
} }
std::string CLabel::formatString(std::string in) { void CLabel::plantTimer() {
replaceAll(in, "$USER", std::string{getlogin()}); if (label.updateEveryMs != 0)
return in; labelTimer = g_pHyprlock->addTimer(std::chrono::milliseconds((int)label.updateEveryMs), onTimer, this);
} }
CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) { CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, CSessionLockSurface* surface_) : surface(surface_) {
std::string labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text")); labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text"));
std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family")); std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
CColor labelColor = std::any_cast<Hyprlang::INT>(props.at("color")); CColor labelColor = std::any_cast<Hyprlang::INT>(props.at("color"));
int fontSize = std::any_cast<Hyprlang::INT>(props.at("font_size")); int fontSize = std::any_cast<Hyprlang::INT>(props.at("font_size"));
CAsyncResourceGatherer::SPreloadRequest request; label = formatString(labelPreFormat);
request.id = std::string{"label:"} + std::to_string((uintptr_t)this);
request.id = std::string{"label:"} + std::to_string((uintptr_t)this) + ",time:" + std::to_string(time(nullptr));
resourceID = request.id; resourceID = request.id;
request.asset = formatString(labelPreFormat); request.asset = label.formatted;
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT; request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
request.props["font_family"] = fontFamily; request.props["font_family"] = fontFamily;
request.props["color"] = labelColor; request.props["color"] = labelColor;
@ -38,12 +65,14 @@ CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string,
auto POS__ = std::any_cast<Hyprlang::VEC2>(props.at("position")); auto POS__ = std::any_cast<Hyprlang::VEC2>(props.at("position"));
pos = {POS__.x, POS__.y}; pos = {POS__.x, POS__.y};
configPos = pos;
viewport = viewport_; viewport = viewport_;
label = request.asset;
halign = std::any_cast<Hyprlang::STRING>(props.at("halign")); halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign")); valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
plantTimer();
} }
bool CLabel::draw(const SRenderData& data) { bool CLabel::draw(const SRenderData& data) {
@ -54,7 +83,20 @@ bool CLabel::draw(const SRenderData& data) {
return true; return true;
// calc pos // calc pos
pos = posFromHVAlign(viewport, asset->texture.m_vSize, pos, halign, valign); pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign);
}
if (!pendingResourceID.empty()) {
// new asset is pending
auto newAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(pendingResourceID);
if (newAsset) {
// new asset is ready :D
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
asset = newAsset;
resourceID = pendingResourceID;
pendingResourceID = "";
pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign);
}
} }
CBox box = {pos.x, pos.y, asset->texture.m_vSize.x, asset->texture.m_vSize.y}; CBox box = {pos.x, pos.y, asset->texture.m_vSize.x, asset->texture.m_vSize.y};
@ -63,3 +105,7 @@ bool CLabel::draw(const SRenderData& data) {
return false; return false;
} }
void CLabel::renderSuper() {
surface->render();
}

View file

@ -2,25 +2,40 @@
#include "IWidget.hpp" #include "IWidget.hpp"
#include "../../helpers/Vector2D.hpp" #include "../../helpers/Vector2D.hpp"
#include "../../core/Timer.hpp"
#include "../AsyncResourceGatherer.hpp"
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <any> #include <any>
struct SPreloadedAsset; struct SPreloadedAsset;
class CSessionLockSurface;
class CLabel : public IWidget { class CLabel : public IWidget {
public: public:
CLabel(const Vector2D& viewport, const std::unordered_map<std::string, std::any>& props); CLabel(const Vector2D& viewport, const std::unordered_map<std::string, std::any>& props, CSessionLockSurface* surface_);
~CLabel();
virtual bool draw(const SRenderData& data); virtual bool draw(const SRenderData& data);
void renderSuper();
void onTimerUpdate();
void plantTimer();
private: private:
std::string formatString(std::string in); std::string labelPreFormat;
IWidget::SFormatResult label;
Vector2D viewport; Vector2D viewport;
Vector2D pos; Vector2D pos;
Vector2D configPos;
std::string resourceID; std::string resourceID;
std::string label; std::string pendingResourceID; // if dynamic label
std::string halign, valign; std::string halign, valign;
SPreloadedAsset* asset = nullptr; SPreloadedAsset* asset = nullptr;
CSessionLockSurface* surface = nullptr;
CAsyncResourceGatherer::SPreloadRequest request;
std::shared_ptr<CTimer> labelTimer;
}; };