label: add executing commands

This commit is contained in:
Vaxry 2024-02-21 14:08:40 +00:00
parent 3d27060688
commit 06b07c53e5
8 changed files with 220 additions and 4 deletions

View file

@ -539,3 +539,73 @@ std::shared_ptr<CTimer> CHyprlock::addTimer(const std::chrono::system_clock::dur
m_sLoopState.timerCV.notify_all();
return T;
}
void CHyprlock::spawnAsync(const std::string& args) {
Debug::log(LOG, "Executing (async) {}", args);
int socket[2];
if (pipe(socket) != 0)
Debug::log(LOG, "Unable to create pipe for fork");
pid_t child, grandchild;
child = fork();
if (child < 0) {
close(socket[0]);
close(socket[1]);
Debug::log(LOG, "Fail to create the first fork");
return;
}
if (child == 0) {
// run in child
sigset_t set;
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
grandchild = fork();
if (grandchild == 0) {
// run in grandchild
close(socket[0]);
close(socket[1]);
execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr);
// exit grandchild
_exit(0);
}
close(socket[0]);
write(socket[1], &grandchild, sizeof(grandchild));
close(socket[1]);
// exit child
_exit(0);
}
// run in parent
close(socket[1]);
read(socket[0], &grandchild, sizeof(grandchild));
close(socket[0]);
// clear child and leave child to init
waitpid(child, NULL, 0);
if (child < 0) {
Debug::log(LOG, "Failed to create the second fork");
return;
}
Debug::log(LOG, "Process Created with pid {}", grandchild);
}
std::string CHyprlock::spawnSync(const std::string& cmd) {
std::array<char, 128> buffer;
std::string result;
const std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe)
return "";
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}

View file

@ -33,6 +33,9 @@ class CHyprlock {
void lockSession();
void unlockSession();
void spawnAsync(const std::string& cmd);
std::string spawnSync(const std::string& cmd);
void onKey(uint32_t key);
void onPasswordCheckTimer();
bool passwordCheckWaiting();

55
src/helpers/VarList.cpp Normal file
View file

@ -0,0 +1,55 @@
#include "VarList.hpp"
#include <ranges>
#include <algorithm>
static std::string removeBeginEndSpacesTabs(std::string str) {
if (str.empty())
return str;
int countBefore = 0;
while (str[countBefore] == ' ' || str[countBefore] == '\t') {
countBefore++;
}
int countAfter = 0;
while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) {
countAfter++;
}
str = str.substr(countBefore, str.length() - countBefore - countAfter);
return str;
}
CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) {
if (in.empty())
m_vArgs.emplace_back("");
std::string args{in};
size_t idx = 0;
size_t pos = 0;
std::ranges::replace_if(
args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
for (const auto& s : args | std::views::split(0)) {
if (removeEmpty && s.empty())
continue;
if (++idx == lastArgNo) {
m_vArgs.emplace_back(removeBeginEndSpacesTabs(in.substr(pos)));
break;
}
pos += s.size() + 1;
m_vArgs.emplace_back(removeBeginEndSpacesTabs(std::string_view{s}.data()));
}
}
std::string CVarList::join(const std::string& joiner, size_t from, size_t to) const {
size_t last = to == 0 ? size() : to;
std::string rolling;
for (size_t i = from; i < last; ++i) {
rolling += m_vArgs[i] + (i + 1 < last ? joiner : "");
}
return rolling;
}

63
src/helpers/VarList.hpp Normal file
View file

@ -0,0 +1,63 @@
#pragma once
#include <functional>
#include <vector>
#include <string>
class CVarList {
public:
/** Split string into arg list
@param lastArgNo stop splitting after argv reaches maximum size, last arg will contain rest of unsplit args
@param delim if delimiter is 's', use std::isspace
@param removeEmpty remove empty args from argv
*/
CVarList(const std::string& in, const size_t maxSize = 0, const char delim = ',', const bool removeEmpty = false);
~CVarList() = default;
size_t size() const {
return m_vArgs.size();
}
std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const;
void map(std::function<void(std::string&)> func) {
for (auto& s : m_vArgs)
func(s);
}
void append(const std::string arg) {
m_vArgs.emplace_back(arg);
}
std::string operator[](const size_t& idx) const {
if (idx >= m_vArgs.size())
return "";
return m_vArgs[idx];
}
// for range-based loops
std::vector<std::string>::iterator begin() {
return m_vArgs.begin();
}
std::vector<std::string>::const_iterator begin() const {
return m_vArgs.begin();
}
std::vector<std::string>::iterator end() {
return m_vArgs.end();
}
std::vector<std::string>::const_iterator end() const {
return m_vArgs.end();
}
bool contains(const std::string& el) {
for (auto& a : m_vArgs) {
if (a == el)
return true;
}
return false;
}
private:
std::vector<std::string> m_vArgs;
};

View file

@ -129,6 +129,8 @@ void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
const int FONTSIZE = rq.props.contains("font_size") ? std::any_cast<int>(rq.props.at("font_size")) : 16;
const CColor FONTCOLOR = rq.props.contains("color") ? std::any_cast<CColor>(rq.props.at("color")) : CColor(1.0, 1.0, 1.0, 1.0);
const std::string FONTFAMILY = rq.props.contains("font_family") ? std::any_cast<std::string>(rq.props.at("font_family")) : "Sans";
const bool ISCMD = rq.props.contains("cmd") ? std::any_cast<bool>(rq.props.at("cmd")) : false;
const std::string TEXT = ISCMD ? g_pHyprlock->spawnSync(rq.asset) : rq.asset;
auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1920, 1080 /* dummy value */);
auto CAIRO = cairo_create(CAIROSURFACE);
@ -144,12 +146,12 @@ void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
PangoAttrList* attrList = nullptr;
GError* gError = nullptr;
char* buf = nullptr;
if (pango_parse_markup(rq.asset.c_str(), -1, 0, &attrList, &buf, nullptr, &gError))
if (pango_parse_markup(TEXT.c_str(), -1, 0, &attrList, &buf, nullptr, &gError))
pango_layout_set_text(layout, buf, -1);
else {
Debug::log(ERR, "Pango markup parsing for {} failed: {}", rq.asset, gError->message);
Debug::log(ERR, "Pango markup parsing for {} failed: {}", TEXT, gError->message);
g_error_free(gError);
pango_layout_set_text(layout, rq.asset.c_str(), -1);
pango_layout_set_text(layout, TEXT.c_str(), -1);
}
if (!attrList)

View file

@ -1,5 +1,6 @@
#include "IWidget.hpp"
#include "../../helpers/Log.hpp"
#include "../../helpers/VarList.hpp"
#include <chrono>
Vector2D IWidget::posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign) {
@ -55,6 +56,25 @@ IWidget::SFormatResult IWidget::formatString(std::string in) {
result.updateEveryMs = result.updateEveryMs != 0 && result.updateEveryMs < 1000 ? result.updateEveryMs : 1000;
}
if (in.starts_with("cmd[") && in.contains("]")) {
// this is a command
CVarList vars(in.substr(4, in.find_first_of(']') - 4));
for (const auto& v : vars) {
if (v.starts_with("update:")) {
try {
result.updateEveryMs = std::stoull(v.substr(7));
} catch (std::exception& e) { Debug::log(ERR, "Error parsing {} in cmd[]", v); }
} else {
Debug::log(ERR, "Unknown prop in string format {}", v);
}
}
result.alwaysUpdate = true;
in = in.substr(in.find_first_of(']') + 1);
result.cmd = true;
}
result.formatted = in;
return result;
}

View file

@ -16,6 +16,8 @@ class IWidget {
struct SFormatResult {
std::string formatted;
float updateEveryMs = 0; // 0 means don't (static)
bool alwaysUpdate = false;
bool cmd = false;
};
virtual SFormatResult formatString(std::string in);

View file

@ -26,7 +26,7 @@ void CLabel::onTimerUpdate() {
label = formatString(labelPreFormat);
if (label.formatted == oldFormatted)
if (label.formatted == oldFormatted && !label.alwaysUpdate)
return;
if (!pendingResourceID.empty())
@ -60,6 +60,7 @@ CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string,
request.props["font_family"] = fontFamily;
request.props["color"] = labelColor;
request.props["font_size"] = fontSize;
request.props["cmd"] = label.cmd;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);