mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-12-22 13:29:48 +01:00
label: add time and dynamic timers
This commit is contained in:
parent
c60b7b8aef
commit
7370fc624f
9 changed files with 255 additions and 38 deletions
|
@ -4,8 +4,12 @@
|
|||
#include "../renderer/Renderer.hpp"
|
||||
#include "Password.hpp"
|
||||
#include "Egl.hpp"
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/mman.h>
|
||||
#include <cuchar>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
CHyprlock::CHyprlock(const std::string& wlDisplay) {
|
||||
m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str());
|
||||
|
@ -110,9 +114,120 @@ void CHyprlock::run() {
|
|||
|
||||
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)
|
||||
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");
|
||||
|
@ -375,3 +490,8 @@ wp_viewporter* CHyprlock::getViewporter() {
|
|||
size_t CHyprlock::getPasswordBufferLen() {
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
#include "viewporter-protocol.h"
|
||||
#include "Output.hpp"
|
||||
#include "CursorShape.hpp"
|
||||
#include "Timer.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <condition_variable>
|
||||
|
||||
#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 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 onLockFinished();
|
||||
|
||||
|
@ -68,7 +72,20 @@ class CHyprlock {
|
|||
std::string passBuffer = "";
|
||||
} 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::shared_ptr<CTimer>> m_vTimers;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CHyprlock> g_pHyprlock;
|
|
@ -14,8 +14,10 @@ int main(int argc, char** argv, char** envp) {
|
|||
else if (arg == "--quiet" || arg == "-q")
|
||||
Debug::quiet = true;
|
||||
|
||||
else if (arg == "--display" && i + 1 < argc)
|
||||
else if (arg == "--display" && i + 1 < argc) {
|
||||
wlDisplay = argv[i + 1];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -89,12 +89,13 @@ void CAsyncResourceGatherer::apply() {
|
|||
|
||||
for (auto& t : preloadTargets) {
|
||||
if (t.type == TARGET_IMAGE) {
|
||||
const auto ASSET = &assets[t.id];
|
||||
std::lock_guard<std::mutex> lg(asyncLoopState.assetsMutex);
|
||||
const auto ASSET = &assets[t.id];
|
||||
|
||||
const auto CAIROFORMAT = cairo_image_surface_get_format((cairo_surface_t*)t.cairosurface);
|
||||
const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA;
|
||||
const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA;
|
||||
const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE;
|
||||
const auto CAIROFORMAT = cairo_image_surface_get_format((cairo_surface_t*)t.cairosurface);
|
||||
const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA;
|
||||
const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA;
|
||||
const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE;
|
||||
|
||||
ASSET->texture.m_vSize = t.size;
|
||||
ASSET->texture.allocate();
|
||||
|
@ -234,3 +235,9 @@ void CAsyncResourceGatherer::requestAsyncAssetPreload(const SPreloadRequest& req
|
|||
asyncLoopState.pending = true;
|
||||
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; });
|
||||
}
|
|
@ -42,6 +42,7 @@ class CAsyncResourceGatherer {
|
|||
};
|
||||
|
||||
void requestAsyncAssetPreload(const SPreloadRequest& request);
|
||||
void unloadAsset(SPreloadedAsset* asset);
|
||||
|
||||
private:
|
||||
std::thread initThread;
|
||||
|
@ -56,6 +57,8 @@ class CAsyncResourceGatherer {
|
|||
|
||||
std::mutex requestMutex;
|
||||
|
||||
std::mutex assetsMutex;
|
||||
|
||||
std::vector<SPreloadRequest> requests;
|
||||
bool pending = false;
|
||||
|
||||
|
|
|
@ -258,7 +258,7 @@ std::vector<std::unique_ptr<IWidget>>* CRenderer::getOrCreateWidgetsFor(const CS
|
|||
} else if (c.type == "input-field") {
|
||||
widgets[surf].emplace_back(std::make_unique<CPasswordInputField>(surf->size, c.values));
|
||||
} 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,4 +12,11 @@ class IWidget {
|
|||
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);
|
||||
|
||||
struct SFormatResult {
|
||||
std::string formatted;
|
||||
float updateEveryMs = 0; // 0 means don't (static)
|
||||
};
|
||||
|
||||
virtual SFormatResult formatString(std::string in);
|
||||
};
|
|
@ -3,32 +3,59 @@
|
|||
#include <hyprlang.hpp>
|
||||
#include "../Renderer.hpp"
|
||||
#include "../../helpers/Log.hpp"
|
||||
#include "../../core/hyprlock.hpp"
|
||||
|
||||
void replaceAll(std::string& str, const std::string& from, const std::string& to) {
|
||||
if (from.empty())
|
||||
CLabel::~CLabel() {
|
||||
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;
|
||||
size_t pos = 0;
|
||||
while ((pos = str.find(from, pos)) != std::string::npos) {
|
||||
str.replace(pos, from.length(), to);
|
||||
pos += to.length();
|
||||
}
|
||||
|
||||
if (!pendingResourceID.empty())
|
||||
return; // too many updates, we'll miss some. Shouldn't happen tbh
|
||||
|
||||
// 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) {
|
||||
replaceAll(in, "$USER", std::string{getlogin()});
|
||||
return in;
|
||||
void CLabel::plantTimer() {
|
||||
if (label.updateEveryMs != 0)
|
||||
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) {
|
||||
std::string labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text"));
|
||||
std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
|
||||
CColor labelColor = std::any_cast<Hyprlang::INT>(props.at("color"));
|
||||
int fontSize = std::any_cast<Hyprlang::INT>(props.at("font_size"));
|
||||
CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, CSessionLockSurface* surface_) : surface(surface_) {
|
||||
labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text"));
|
||||
std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
|
||||
CColor labelColor = std::any_cast<Hyprlang::INT>(props.at("color"));
|
||||
int fontSize = std::any_cast<Hyprlang::INT>(props.at("font_size"));
|
||||
|
||||
CAsyncResourceGatherer::SPreloadRequest request;
|
||||
request.id = std::string{"label:"} + std::to_string((uintptr_t)this);
|
||||
label = formatString(labelPreFormat);
|
||||
|
||||
request.id = std::string{"label:"} + std::to_string((uintptr_t)this) + ",time:" + std::to_string(time(nullptr));
|
||||
resourceID = request.id;
|
||||
request.asset = formatString(labelPreFormat);
|
||||
request.asset = label.formatted;
|
||||
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
|
||||
request.props["font_family"] = fontFamily;
|
||||
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"));
|
||||
pos = {POS__.x, POS__.y};
|
||||
configPos = pos;
|
||||
|
||||
viewport = viewport_;
|
||||
label = request.asset;
|
||||
|
||||
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
|
||||
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
|
||||
|
||||
plantTimer();
|
||||
}
|
||||
|
||||
bool CLabel::draw(const SRenderData& data) {
|
||||
|
@ -54,7 +83,20 @@ bool CLabel::draw(const SRenderData& data) {
|
|||
return true;
|
||||
|
||||
// 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};
|
||||
|
@ -63,3 +105,7 @@ bool CLabel::draw(const SRenderData& data) {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CLabel::renderSuper() {
|
||||
surface->render();
|
||||
}
|
|
@ -2,25 +2,40 @@
|
|||
|
||||
#include "IWidget.hpp"
|
||||
#include "../../helpers/Vector2D.hpp"
|
||||
#include "../../core/Timer.hpp"
|
||||
#include "../AsyncResourceGatherer.hpp"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <any>
|
||||
|
||||
struct SPreloadedAsset;
|
||||
class CSessionLockSurface;
|
||||
|
||||
class CLabel : public IWidget {
|
||||
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);
|
||||
|
||||
private:
|
||||
std::string formatString(std::string in);
|
||||
void renderSuper();
|
||||
void onTimerUpdate();
|
||||
void plantTimer();
|
||||
|
||||
Vector2D viewport;
|
||||
Vector2D pos;
|
||||
std::string resourceID;
|
||||
std::string label;
|
||||
std::string halign, valign;
|
||||
SPreloadedAsset* asset = nullptr;
|
||||
private:
|
||||
std::string labelPreFormat;
|
||||
IWidget::SFormatResult label;
|
||||
|
||||
Vector2D viewport;
|
||||
Vector2D pos;
|
||||
Vector2D configPos;
|
||||
std::string resourceID;
|
||||
std::string pendingResourceID; // if dynamic label
|
||||
std::string halign, valign;
|
||||
SPreloadedAsset* asset = nullptr;
|
||||
CSessionLockSurface* surface = nullptr;
|
||||
|
||||
CAsyncResourceGatherer::SPreloadRequest request;
|
||||
|
||||
std::shared_ptr<CTimer> labelTimer;
|
||||
};
|
Loading…
Reference in a new issue