widgets: extend shell cmd exec to Label

This commit is contained in:
Bren 2025-01-15 15:13:48 -08:00
parent 02639c2759
commit a0a59aeef2
4 changed files with 135 additions and 4 deletions

View file

@ -30,11 +30,92 @@ class CLayoutValueData : public ICustomConfigValueData {
CLayoutValueData() {};
virtual ~CLayoutValueData() {};
struct SExecuteResult {
Hyprutils::Math::Vector2D values;
struct {
bool x = false;
bool y = false;
} isRelative;
};
SExecuteResult executeCommand() {
if (!m_bIsCommand)
return {{0,0}, {false, false}};
FILE* pipe = popen(m_sCommand.c_str(), "r");
if (!pipe) {
Debug::log(ERR, "Failed to execute position command");
return {{0,0}, {false, false}};
}
char buffer[128];
std::string result = "";
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
pclose(pipe);
if (!result.empty() && result.back() == '\n')
result.pop_back();
SExecuteResult out;
const auto SPLIT = result.find(',');
if (SPLIT == std::string::npos) {
Debug::log(ERR, "Position command output must be x,y format");
return {{0,0}, {false, false}};
}
auto lhs = result.substr(0, SPLIT);
auto rhs = result.substr(SPLIT + 1);
if (rhs.starts_with(" "))
rhs = rhs.substr(1);
if (lhs.ends_with("%")) {
out.isRelative.x = true;
lhs.pop_back();
}
if (rhs.ends_with("%")) {
out.isRelative.y = true;
rhs.pop_back();
}
try {
out.values = {std::stof(lhs), std::stof(rhs)};
} catch (std::exception& e) {
Debug::log(ERR, "Failed to parse command output as coordinates");
return {{0,0}, {false, false}};
}
return out;
}
bool needsUpdate() const {
return m_bIsCommand;
}
float updateMs() const {
return m_bIsCommand ? m_iUpdateMs : 0;
}
void update() {
if (!m_bIsCommand)
return;
auto result = executeCommand();
m_vValues = result.values;
m_sIsRelative.x = result.isRelative.x;
m_sIsRelative.y = result.isRelative.y;
}
virtual eConfigValueDataTypes getDataType() {
return CVD_TYPE_LAYOUT;
}
virtual std::string toString() {
if (m_bIsCommand)
return std::format("cmd[update:{}]{}", m_iUpdateMs, m_sCommand);
return std::format("{}{},{}{}", m_vValues.x, (m_sIsRelative.x) ? "%" : "px", m_vValues.y, (m_sIsRelative.y) ? "%" : "px");
}
@ -46,6 +127,8 @@ class CLayoutValueData : public ICustomConfigValueData {
}
Hyprutils::Math::Vector2D getAbsolute(const Hyprutils::Math::Vector2D& viewport) {
if (m_bIsCommand)
update();
return {
(m_sIsRelative.x ? (m_vValues.x / 100) * viewport.x : m_vValues.x),
(m_sIsRelative.y ? (m_vValues.y / 100) * viewport.y : m_vValues.y),
@ -57,6 +140,10 @@ class CLayoutValueData : public ICustomConfigValueData {
bool x = false;
bool y = false;
} m_sIsRelative;
bool m_bIsCommand = false;
std::string m_sCommand = "";
int m_iUpdateMs = 0;
};
class CGradientValueData : public ICustomConfigValueData {

View file

@ -63,7 +63,36 @@ static Hyprlang::CParseResult configHandleLayoutOption(const char* v, void** dat
if (!*data)
*data = new CLayoutValueData();
const auto DATA = (CLayoutValueData*)(*data);
const auto DATA = (CLayoutValueData*)(*data);
if (VALUE.starts_with("cmd[") && VALUE.contains("]")) {
DATA->m_bIsCommand = true;
std::string options = VALUE.substr(4, VALUE.find_first_of(']') - 4);
CVarList vars(options, 0, ',', true);
for (const auto& var : vars) {
if (var.starts_with("update:")) {
try {
DATA->m_iUpdateMs = std::stoull(var.substr(7));
} catch (std::exception& e) {
Debug::log(ERR, "Error parsing {} in cmd[]", var);
}
} else {
Debug::log(ERR, "Unknown prop in layout format {}", var);
}
}
DATA->m_sCommand = VALUE.substr(VALUE.find_first_of(']') + 1);
auto cmdResult = DATA->executeCommand();
DATA->m_vValues = cmdResult.values;
DATA->m_sIsRelative.x = cmdResult.isRelative.x;
DATA->m_sIsRelative.y = cmdResult.isRelative.y;
return result;
}
const auto SPLIT = VALUE.find(',');
if (SPLIT == std::string::npos) {
result.setError(std::format("expected two comma seperated values, got {}", VALUE).c_str());

View file

@ -44,6 +44,18 @@ void CLabel::onTimerUpdate() {
label = formatString(labelPreFormat);
try {
const auto LAYOUT = CLayoutValueData::fromAnyPv(props.at("position"));
if (LAYOUT->needsUpdate()) {
const auto OLD_POS = LAYOUT->getAbsolute(viewport);
LAYOUT->update();
if (OLD_POS != LAYOUT->getAbsolute(viewport)) { // redraw:
configPos = LAYOUT->getAbsolute(viewport);
g_pHyprlock->renderOutput(outputStringPort);
}
}
} catch (...) { /* TODO */ }
if (label.formatted == oldFormatted && !label.alwaysUpdate)
return;
@ -70,8 +82,8 @@ void CLabel::plantTimer() {
labelTimer = g_pHyprlock->addTimer(std::chrono::hours(1), onTimer, this, true);
}
CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, const std::string& output) :
outputStringPort(output), shadow(this, props, viewport_) {
CLabel::CLabel(const Vector2D& viewport_, const WidgetProps& props_, const std::string& output) :
outputStringPort(output), shadow(this, props_, viewport_), props(props_) {
try {
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_);
labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text"));

View file

@ -12,9 +12,11 @@
struct SPreloadedAsset;
class CSessionLockSurface;
using WidgetProps = std::unordered_map<std::string, std::any>;
class CLabel : public IWidget {
public:
CLabel(const Vector2D& viewport, const std::unordered_map<std::string, std::any>& props, const std::string& output);
CLabel(const Vector2D& viewport_, const WidgetProps& props_, const std::string& output);
~CLabel();
virtual bool draw(const SRenderData& data);
@ -46,4 +48,5 @@ class CLabel : public IWidget {
CShadowable shadow;
bool updateShadow = true;
WidgetProps props;
};