Skip to content

Commit 06b07c5

Browse files
committed
label: add executing commands
1 parent 3d27060 commit 06b07c5

File tree

8 files changed

+220
-4
lines changed

8 files changed

+220
-4
lines changed

src/core/hyprlock.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,3 +539,73 @@ std::shared_ptr<CTimer> CHyprlock::addTimer(const std::chrono::system_clock::dur
539539
m_sLoopState.timerCV.notify_all();
540540
return T;
541541
}
542+
543+
void CHyprlock::spawnAsync(const std::string& args) {
544+
Debug::log(LOG, "Executing (async) {}", args);
545+
546+
int socket[2];
547+
if (pipe(socket) != 0)
548+
Debug::log(LOG, "Unable to create pipe for fork");
549+
550+
pid_t child, grandchild;
551+
child = fork();
552+
553+
if (child < 0) {
554+
close(socket[0]);
555+
close(socket[1]);
556+
Debug::log(LOG, "Fail to create the first fork");
557+
return;
558+
}
559+
560+
if (child == 0) {
561+
// run in child
562+
563+
sigset_t set;
564+
sigemptyset(&set);
565+
sigprocmask(SIG_SETMASK, &set, NULL);
566+
567+
grandchild = fork();
568+
569+
if (grandchild == 0) {
570+
// run in grandchild
571+
close(socket[0]);
572+
close(socket[1]);
573+
execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr);
574+
// exit grandchild
575+
_exit(0);
576+
}
577+
578+
close(socket[0]);
579+
write(socket[1], &grandchild, sizeof(grandchild));
580+
close(socket[1]);
581+
// exit child
582+
_exit(0);
583+
}
584+
585+
// run in parent
586+
close(socket[1]);
587+
read(socket[0], &grandchild, sizeof(grandchild));
588+
close(socket[0]);
589+
// clear child and leave child to init
590+
waitpid(child, NULL, 0);
591+
592+
if (child < 0) {
593+
Debug::log(LOG, "Failed to create the second fork");
594+
return;
595+
}
596+
597+
Debug::log(LOG, "Process Created with pid {}", grandchild);
598+
}
599+
600+
std::string CHyprlock::spawnSync(const std::string& cmd) {
601+
std::array<char, 128> buffer;
602+
std::string result;
603+
const std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
604+
if (!pipe)
605+
return "";
606+
607+
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
608+
result += buffer.data();
609+
}
610+
return result;
611+
}

src/core/hyprlock.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class CHyprlock {
3333
void lockSession();
3434
void unlockSession();
3535

36+
void spawnAsync(const std::string& cmd);
37+
std::string spawnSync(const std::string& cmd);
38+
3639
void onKey(uint32_t key);
3740
void onPasswordCheckTimer();
3841
bool passwordCheckWaiting();

src/helpers/VarList.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "VarList.hpp"
2+
#include <ranges>
3+
#include <algorithm>
4+
5+
static std::string removeBeginEndSpacesTabs(std::string str) {
6+
if (str.empty())
7+
return str;
8+
9+
int countBefore = 0;
10+
while (str[countBefore] == ' ' || str[countBefore] == '\t') {
11+
countBefore++;
12+
}
13+
14+
int countAfter = 0;
15+
while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) {
16+
countAfter++;
17+
}
18+
19+
str = str.substr(countBefore, str.length() - countBefore - countAfter);
20+
21+
return str;
22+
}
23+
24+
CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) {
25+
if (in.empty())
26+
m_vArgs.emplace_back("");
27+
28+
std::string args{in};
29+
size_t idx = 0;
30+
size_t pos = 0;
31+
std::ranges::replace_if(
32+
args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
33+
34+
for (const auto& s : args | std::views::split(0)) {
35+
if (removeEmpty && s.empty())
36+
continue;
37+
if (++idx == lastArgNo) {
38+
m_vArgs.emplace_back(removeBeginEndSpacesTabs(in.substr(pos)));
39+
break;
40+
}
41+
pos += s.size() + 1;
42+
m_vArgs.emplace_back(removeBeginEndSpacesTabs(std::string_view{s}.data()));
43+
}
44+
}
45+
46+
std::string CVarList::join(const std::string& joiner, size_t from, size_t to) const {
47+
size_t last = to == 0 ? size() : to;
48+
49+
std::string rolling;
50+
for (size_t i = from; i < last; ++i) {
51+
rolling += m_vArgs[i] + (i + 1 < last ? joiner : "");
52+
}
53+
54+
return rolling;
55+
}

src/helpers/VarList.hpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#pragma once
2+
#include <functional>
3+
#include <vector>
4+
#include <string>
5+
6+
class CVarList {
7+
public:
8+
/** Split string into arg list
9+
@param lastArgNo stop splitting after argv reaches maximum size, last arg will contain rest of unsplit args
10+
@param delim if delimiter is 's', use std::isspace
11+
@param removeEmpty remove empty args from argv
12+
*/
13+
CVarList(const std::string& in, const size_t maxSize = 0, const char delim = ',', const bool removeEmpty = false);
14+
15+
~CVarList() = default;
16+
17+
size_t size() const {
18+
return m_vArgs.size();
19+
}
20+
21+
std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const;
22+
23+
void map(std::function<void(std::string&)> func) {
24+
for (auto& s : m_vArgs)
25+
func(s);
26+
}
27+
28+
void append(const std::string arg) {
29+
m_vArgs.emplace_back(arg);
30+
}
31+
32+
std::string operator[](const size_t& idx) const {
33+
if (idx >= m_vArgs.size())
34+
return "";
35+
return m_vArgs[idx];
36+
}
37+
38+
// for range-based loops
39+
std::vector<std::string>::iterator begin() {
40+
return m_vArgs.begin();
41+
}
42+
std::vector<std::string>::const_iterator begin() const {
43+
return m_vArgs.begin();
44+
}
45+
std::vector<std::string>::iterator end() {
46+
return m_vArgs.end();
47+
}
48+
std::vector<std::string>::const_iterator end() const {
49+
return m_vArgs.end();
50+
}
51+
52+
bool contains(const std::string& el) {
53+
for (auto& a : m_vArgs) {
54+
if (a == el)
55+
return true;
56+
}
57+
58+
return false;
59+
}
60+
61+
private:
62+
std::vector<std::string> m_vArgs;
63+
};

src/renderer/AsyncResourceGatherer.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
129129
const int FONTSIZE = rq.props.contains("font_size") ? std::any_cast<int>(rq.props.at("font_size")) : 16;
130130
const CColor FONTCOLOR = rq.props.contains("color") ? std::any_cast<CColor>(rq.props.at("color")) : CColor(1.0, 1.0, 1.0, 1.0);
131131
const std::string FONTFAMILY = rq.props.contains("font_family") ? std::any_cast<std::string>(rq.props.at("font_family")) : "Sans";
132+
const bool ISCMD = rq.props.contains("cmd") ? std::any_cast<bool>(rq.props.at("cmd")) : false;
133+
const std::string TEXT = ISCMD ? g_pHyprlock->spawnSync(rq.asset) : rq.asset;
132134

133135
auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1920, 1080 /* dummy value */);
134136
auto CAIRO = cairo_create(CAIROSURFACE);
@@ -144,12 +146,12 @@ void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
144146
PangoAttrList* attrList = nullptr;
145147
GError* gError = nullptr;
146148
char* buf = nullptr;
147-
if (pango_parse_markup(rq.asset.c_str(), -1, 0, &attrList, &buf, nullptr, &gError))
149+
if (pango_parse_markup(TEXT.c_str(), -1, 0, &attrList, &buf, nullptr, &gError))
148150
pango_layout_set_text(layout, buf, -1);
149151
else {
150-
Debug::log(ERR, "Pango markup parsing for {} failed: {}", rq.asset, gError->message);
152+
Debug::log(ERR, "Pango markup parsing for {} failed: {}", TEXT, gError->message);
151153
g_error_free(gError);
152-
pango_layout_set_text(layout, rq.asset.c_str(), -1);
154+
pango_layout_set_text(layout, TEXT.c_str(), -1);
153155
}
154156

155157
if (!attrList)

src/renderer/widgets/IWidget.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "IWidget.hpp"
22
#include "../../helpers/Log.hpp"
3+
#include "../../helpers/VarList.hpp"
34
#include <chrono>
45

56
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) {
5556
result.updateEveryMs = result.updateEveryMs != 0 && result.updateEveryMs < 1000 ? result.updateEveryMs : 1000;
5657
}
5758

59+
if (in.starts_with("cmd[") && in.contains("]")) {
60+
// this is a command
61+
CVarList vars(in.substr(4, in.find_first_of(']') - 4));
62+
63+
for (const auto& v : vars) {
64+
if (v.starts_with("update:")) {
65+
try {
66+
result.updateEveryMs = std::stoull(v.substr(7));
67+
} catch (std::exception& e) { Debug::log(ERR, "Error parsing {} in cmd[]", v); }
68+
} else {
69+
Debug::log(ERR, "Unknown prop in string format {}", v);
70+
}
71+
}
72+
73+
result.alwaysUpdate = true;
74+
in = in.substr(in.find_first_of(']') + 1);
75+
result.cmd = true;
76+
}
77+
5878
result.formatted = in;
5979
return result;
6080
}

src/renderer/widgets/IWidget.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class IWidget {
1616
struct SFormatResult {
1717
std::string formatted;
1818
float updateEveryMs = 0; // 0 means don't (static)
19+
bool alwaysUpdate = false;
20+
bool cmd = false;
1921
};
2022

2123
virtual SFormatResult formatString(std::string in);

src/renderer/widgets/Label.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ void CLabel::onTimerUpdate() {
2626

2727
label = formatString(labelPreFormat);
2828

29-
if (label.formatted == oldFormatted)
29+
if (label.formatted == oldFormatted && !label.alwaysUpdate)
3030
return;
3131

3232
if (!pendingResourceID.empty())
@@ -60,6 +60,7 @@ CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string,
6060
request.props["font_family"] = fontFamily;
6161
request.props["color"] = labelColor;
6262
request.props["font_size"] = fontSize;
63+
request.props["cmd"] = label.cmd;
6364

6465
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
6566

0 commit comments

Comments
 (0)