summaryrefslogtreecommitdiff
path: root/src/config/lua
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2026-04-28 18:10:25 +0100
committerGitHub <noreply@github.com>2026-04-28 18:10:25 +0100
commit94b90a2cb0b4a0e13018cd5ebbf09394abf3b96b (patch)
tree484d32baaab1acdaab7a817c86aa89f117a21059 /src/config/lua
parentab75bbfbecdfb4a0355106f740944c87cb9edd4c (diff)
config/lua: add ExpressionVec2, allow using a table for vec2 rules (#14197)
Diffstat (limited to 'src/config/lua')
-rw-r--r--src/config/lua/bindings/LuaBindingsConfigRules.cpp18
-rw-r--r--src/config/lua/bindings/LuaBindingsInternal.cpp44
-rw-r--r--src/config/lua/bindings/LuaBindingsInternal.hpp14
-rw-r--r--src/config/lua/types/LuaConfigExpressionVec2.cpp100
-rw-r--r--src/config/lua/types/LuaConfigExpressionVec2.hpp26
5 files changed, 163 insertions, 39 deletions
diff --git a/src/config/lua/bindings/LuaBindingsConfigRules.cpp b/src/config/lua/bindings/LuaBindingsConfigRules.cpp
index 88761f05..52ecaf69 100644
--- a/src/config/lua/bindings/LuaBindingsConfigRules.cpp
+++ b/src/config/lua/bindings/LuaBindingsConfigRules.cpp
@@ -1076,21 +1076,9 @@ static int hlWindowRule(lua_State* L) {
continue;
}
- auto val = UP<ILuaConfigValue>(desc->factory());
- auto err = val->parse(L);
- if (err.errorCode != PARSE_ERROR_OK) {
- const bool allowLegacyString = (key == "max_size" || key == "min_size" || key == "border_color") && lua_isstring(L, -1);
- if (allowLegacyString) {
- auto res = rule->addEffect(desc->effect, lua_tostring(L, -1));
- if (!res)
- self->addError(std::format("{}: hl.window_rule: field '{}': {}", sourceInfo, key, res.error()));
- } else
- self->addError(std::format("{}: hl.window_rule: field '{}': {}", sourceInfo, key, err.message));
- } else {
- auto res = rule->addEffect(desc->effect, val->toString());
- if (!res)
- self->addError(std::format("{}: hl.window_rule: field '{}': {}", sourceInfo, key, res.error()));
- }
+ auto res = Internal::addWindowRuleEffectFromLua(L, *desc, rule);
+ if (!res)
+ self->addError(std::format("{}: hl.window_rule: field '{}': {}", sourceInfo, key, res.error()));
lua_pop(L, 1);
}
diff --git a/src/config/lua/bindings/LuaBindingsInternal.cpp b/src/config/lua/bindings/LuaBindingsInternal.cpp
index e5a65bf7..a6af42f8 100644
--- a/src/config/lua/bindings/LuaBindingsInternal.cpp
+++ b/src/config/lua/bindings/LuaBindingsInternal.cpp
@@ -1,5 +1,7 @@
#include "LuaBindingsInternal.hpp"
+#include "../../../desktop/rule/windowRule/WindowRule.hpp"
+
using namespace Config;
using namespace Config::Lua;
using namespace Config::Lua::Bindings;
@@ -489,6 +491,24 @@ std::expected<std::string, std::string> Internal::ruleValueToString(lua_State* L
return std::unexpected("value must be a string, bool, or number");
}
+std::expected<void, std::string> Internal::addWindowRuleEffectFromLua(lua_State* L, const SWindowRuleEffectDesc& desc, const SP<Desktop::Rule::CWindowRule>& rule) {
+ auto val = UP<ILuaConfigValue>(desc.factory());
+ auto err = val->parse(L);
+
+ if (err.errorCode != PARSE_ERROR_OK) {
+ const bool allowLegacyString = desc.effect == WE::WINDOW_RULE_EFFECT_BORDER_COLOR && lua_isstring(L, -1);
+ if (!allowLegacyString)
+ return std::unexpected(err.message);
+
+ return rule->addEffect(desc.effect, lua_tostring(L, -1));
+ }
+
+ if (const auto expr = dc<CLuaConfigExpressionVec2*>(val.get()); expr)
+ return rule->addEffect(desc.effect, expr->parsed());
+
+ return rule->addEffect(desc.effect, val->toString());
+}
+
std::expected<SP<Desktop::Rule::CWindowRule>, int> Internal::buildRuleFromTable(lua_State* L, int idx) {
SP<Desktop::Rule::CWindowRule> rule;
@@ -538,26 +558,10 @@ std::expected<SP<Desktop::Rule::CWindowRule>, int> Internal::buildRuleFromTable(
continue;
}
- auto val = UP<ILuaConfigValue>(desc->factory());
- auto err = val->parse(L);
- if (err.errorCode != PARSE_ERROR_OK) {
- const bool allowLegacyString = (key == "max_size" || key == "min_size" || key == "border_color") && lua_isstring(L, -1);
- if (allowLegacyString) {
- auto res = rule->addEffect(desc->effect, lua_tostring(L, -1));
- if (!res) {
- lua_pop(L, 1);
- return std::unexpected(Internal::configError(L, "buildRuleFromTable: effect '{}': {}", key, res.error()));
- }
- } else {
- lua_pop(L, 1);
- return std::unexpected(Internal::configError(L, "buildRuleFromTable: effect '{}': {}", key, err.message));
- }
- } else {
- auto res = rule->addEffect(desc->effect, val->toString());
- if (!res) {
- lua_pop(L, 1);
- return std::unexpected(Internal::configError(L, "buildRuleFromTable: effect '{}': {}", key, res.error()));
- }
+ auto res = Internal::addWindowRuleEffectFromLua(L, *desc, rule);
+ if (!res) {
+ lua_pop(L, 1);
+ return std::unexpected(Internal::configError(L, "buildRuleFromTable: effect '{}': {}", key, res.error()));
}
hasRuleEffects = true;
diff --git a/src/config/lua/bindings/LuaBindingsInternal.hpp b/src/config/lua/bindings/LuaBindingsInternal.hpp
index fc07515f..5d4a8d87 100644
--- a/src/config/lua/bindings/LuaBindingsInternal.hpp
+++ b/src/config/lua/bindings/LuaBindingsInternal.hpp
@@ -10,6 +10,7 @@
#include "../types/LuaConfigInt.hpp"
#include "../types/LuaConfigString.hpp"
#include "../types/LuaConfigVec2.hpp"
+#include "../types/LuaConfigExpressionVec2.hpp"
#include "../../../Compositor.hpp"
#include "../../../helpers/MiscFunctions.hpp"
@@ -31,6 +32,10 @@ extern "C" {
#include <lauxlib.h>
}
+namespace Desktop::Rule {
+ class CWindowRule;
+}
+
namespace Config::Lua::Bindings::Internal {
struct SWindowRuleEffectDesc {
@@ -51,8 +56,8 @@ namespace Config::Lua::Bindings::Internal {
{"no_initial_focus", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_NOINITIALFOCUS},
{"pin", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_PIN},
{"fullscreen_state", []() -> ILuaConfigValue* { return new CLuaConfigString(STRVAL_EMPTY); }, WE::WINDOW_RULE_EFFECT_FULLSCREENSTATE},
- {"move", []() -> ILuaConfigValue* { return new CLuaConfigString(STRVAL_EMPTY); }, WE::WINDOW_RULE_EFFECT_MOVE},
- {"size", []() -> ILuaConfigValue* { return new CLuaConfigString(STRVAL_EMPTY); }, WE::WINDOW_RULE_EFFECT_SIZE},
+ {"move", []() -> ILuaConfigValue* { return new CLuaConfigExpressionVec2(); }, WE::WINDOW_RULE_EFFECT_MOVE},
+ {"size", []() -> ILuaConfigValue* { return new CLuaConfigExpressionVec2(); }, WE::WINDOW_RULE_EFFECT_SIZE},
{"monitor", []() -> ILuaConfigValue* { return new CLuaConfigString(STRVAL_EMPTY); }, WE::WINDOW_RULE_EFFECT_MONITOR},
{"workspace", []() -> ILuaConfigValue* { return new CLuaConfigString(STRVAL_EMPTY); }, WE::WINDOW_RULE_EFFECT_WORKSPACE},
{"group", []() -> ILuaConfigValue* { return new CLuaConfigString(STRVAL_EMPTY); }, WE::WINDOW_RULE_EFFECT_GROUP},
@@ -69,8 +74,8 @@ namespace Config::Lua::Bindings::Internal {
{"idle_inhibit", []() -> ILuaConfigValue* { return new CLuaConfigString(STRVAL_EMPTY); }, WE::WINDOW_RULE_EFFECT_IDLE_INHIBIT},
{"opacity", []() -> ILuaConfigValue* { return new CLuaConfigString(STRVAL_EMPTY); }, WE::WINDOW_RULE_EFFECT_OPACITY},
{"tag", []() -> ILuaConfigValue* { return new CLuaConfigString(STRVAL_EMPTY); }, WE::WINDOW_RULE_EFFECT_TAG},
- {"max_size", []() -> ILuaConfigValue* { return new CLuaConfigVec2({0, 0}); }, WE::WINDOW_RULE_EFFECT_MAX_SIZE},
- {"min_size", []() -> ILuaConfigValue* { return new CLuaConfigVec2({0, 0}); }, WE::WINDOW_RULE_EFFECT_MIN_SIZE},
+ {"max_size", []() -> ILuaConfigValue* { return new CLuaConfigExpressionVec2(); }, WE::WINDOW_RULE_EFFECT_MAX_SIZE},
+ {"min_size", []() -> ILuaConfigValue* { return new CLuaConfigExpressionVec2(); }, WE::WINDOW_RULE_EFFECT_MIN_SIZE},
{"border_color", []() -> ILuaConfigValue* { return new CLuaConfigGradient(CHyprColor(0xFF000000)); }, WE::WINDOW_RULE_EFFECT_BORDER_COLOR},
{"persistent_size", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_PERSISTENT_SIZE},
{"allows_input", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_ALLOWS_INPUT},
@@ -140,6 +145,7 @@ namespace Config::Lua::Bindings::Internal {
Config::Actions::eTogglableAction tableToggleAction(lua_State* L, int idx, const char* field = "action");
std::expected<std::string, std::string> ruleValueToString(lua_State* L);
+ std::expected<void, std::string> addWindowRuleEffectFromLua(lua_State* L, const SWindowRuleEffectDesc& desc, const SP<Desktop::Rule::CWindowRule>& rule);
std::expected<SP<Desktop::Rule::CWindowRule>, int> buildRuleFromTable(lua_State* L, int idx);
int configError(lua_State* L, std::string s, Config::Actions::eActionErrorLevel level = Config::Actions::eActionErrorLevel::ERROR,
diff --git a/src/config/lua/types/LuaConfigExpressionVec2.cpp b/src/config/lua/types/LuaConfigExpressionVec2.cpp
new file mode 100644
index 00000000..baabf02f
--- /dev/null
+++ b/src/config/lua/types/LuaConfigExpressionVec2.cpp
@@ -0,0 +1,100 @@
+#include "LuaConfigExpressionVec2.hpp"
+
+#include <format>
+#include <utility>
+
+using namespace Config;
+using namespace Config::Lua;
+
+CLuaConfigExpressionVec2::CLuaConfigExpressionVec2(Math::SExpressionVec2 def) : m_default(std::move(def)), m_data(m_default) {
+ ;
+}
+
+static std::expected<std::string, std::string> expressionVec2ElementToString(lua_State* s, int idx) {
+ if (lua_isinteger(s, idx))
+ return std::to_string(lua_tointeger(s, idx));
+
+ if (lua_isnumber(s, idx))
+ return std::format("{}", lua_tonumber(s, idx));
+
+ if (lua_isstring(s, idx))
+ return std::string{lua_tostring(s, idx)};
+
+ return std::unexpected("expression vec2 elements must be strings or numbers");
+}
+
+SParseError CLuaConfigExpressionVec2::parse(lua_State* s) {
+ Math::SExpressionVec2 vec;
+
+ if (lua_isstring(s, -1)) {
+ auto parsed = Math::parseExpressionVec2(lua_tostring(s, -1));
+ if (!parsed)
+ return {.errorCode = PARSE_ERROR_BAD_VALUE, .message = parsed.error()};
+
+ vec = std::move(*parsed);
+ } else {
+ if (!lua_istable(s, -1))
+ return {.errorCode = PARSE_ERROR_BAD_TYPE, .message = "expression vec2 type requires an array or string"};
+
+ if (lua_rawlen(s, -1) != 2)
+ return {.errorCode = PARSE_ERROR_BAD_VALUE, .message = "expression vec2 type requires exactly 2 elements"};
+
+ lua_rawgeti(s, -1, 1);
+ auto x = expressionVec2ElementToString(s, -1);
+ lua_pop(s, 1);
+ if (!x)
+ return {.errorCode = PARSE_ERROR_BAD_TYPE, .message = x.error()};
+
+ lua_rawgeti(s, -1, 2);
+ auto y = expressionVec2ElementToString(s, -1);
+ lua_pop(s, 1);
+ if (!y)
+ return {.errorCode = PARSE_ERROR_BAD_TYPE, .message = y.error()};
+
+ if (x->empty() || y->empty())
+ return {.errorCode = PARSE_ERROR_BAD_VALUE, .message = "expression vec2 elements must not be empty"};
+
+ vec = {std::move(*x), std::move(*y)};
+ }
+
+ m_data = std::move(vec);
+ m_bSetByUser = true;
+
+ return {.errorCode = PARSE_ERROR_OK};
+}
+
+const std::type_info* CLuaConfigExpressionVec2::underlying() {
+ return &typeid(decltype(m_data));
+}
+
+void const* CLuaConfigExpressionVec2::data() {
+ return &m_data;
+}
+
+std::string CLuaConfigExpressionVec2::toString() {
+ return m_data.toString();
+}
+
+void CLuaConfigExpressionVec2::push(lua_State* s) {
+ lua_createtable(s, 2, 2);
+
+ lua_pushstring(s, m_data.x.c_str());
+ lua_rawseti(s, -2, 1);
+
+ lua_pushstring(s, m_data.y.c_str());
+ lua_rawseti(s, -2, 2);
+
+ lua_pushstring(s, m_data.x.c_str());
+ lua_setfield(s, -2, "x");
+
+ lua_pushstring(s, m_data.y.c_str());
+ lua_setfield(s, -2, "y");
+}
+
+const Math::SExpressionVec2& CLuaConfigExpressionVec2::parsed() {
+ return m_data;
+}
+
+void CLuaConfigExpressionVec2::reset() {
+ m_data = m_default;
+}
diff --git a/src/config/lua/types/LuaConfigExpressionVec2.hpp b/src/config/lua/types/LuaConfigExpressionVec2.hpp
new file mode 100644
index 00000000..cf8b398d
--- /dev/null
+++ b/src/config/lua/types/LuaConfigExpressionVec2.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "LuaConfigValue.hpp"
+
+#include "../../../helpers/math/Expression.hpp"
+
+namespace Config::Lua {
+ class CLuaConfigExpressionVec2 : public ILuaConfigValue {
+ public:
+ CLuaConfigExpressionVec2(Math::SExpressionVec2 def = {});
+ virtual ~CLuaConfigExpressionVec2() = default;
+
+ virtual SParseError parse(lua_State* s);
+ virtual const std::type_info* underlying();
+ virtual void const* data();
+ virtual std::string toString();
+ virtual void push(lua_State* s);
+ virtual void reset();
+
+ const Math::SExpressionVec2& parsed();
+
+ private:
+ Math::SExpressionVec2 m_default;
+ Math::SExpressionVec2 m_data;
+ };
+};