summaryrefslogtreecommitdiff
path: root/src/config
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2026-04-28 15:56:07 +0100
committerGitHub <noreply@github.com>2026-04-28 15:56:07 +0100
commit98fbbafef78057c818fb02b15c27e38ad2f5b5bc (patch)
treec62aa1eae704e7595d58f7edeb3804e8bfd0099d /src/config
parentd4c6ff434c5c7aa66bc7c28e5ce336d2a694bfcc (diff)
config/errors: Report and categorize errors properly for actions (#14192)
Makes all errors categorized, some not displayed, some displayed. All errors can be obtained after hl.dispatch()
Diffstat (limited to 'src/config')
-rw-r--r--src/config/legacy/DispatcherTranslator.cpp2
-rw-r--r--src/config/lua/ConfigManager.cpp24
-rw-r--r--src/config/lua/ConfigManager.hpp3
-rw-r--r--src/config/lua/bindings/LuaBindingsDispatchers.cpp272
-rw-r--r--src/config/lua/bindings/LuaBindingsInternal.cpp69
-rw-r--r--src/config/lua/bindings/LuaBindingsInternal.hpp17
-rw-r--r--src/config/lua/bindings/LuaBindingsRegistration.cpp39
-rw-r--r--src/config/lua/bindings/LuaBindingsToplevel.cpp14
-rw-r--r--src/config/shared/ConfigErrors.hpp69
-rw-r--r--src/config/shared/actions/ConfigActions.cpp68
-rw-r--r--src/config/shared/actions/ConfigActions.hpp12
11 files changed, 370 insertions, 219 deletions
diff --git a/src/config/legacy/DispatcherTranslator.cpp b/src/config/legacy/DispatcherTranslator.cpp
index 68719dc8..0521f161 100644
--- a/src/config/legacy/DispatcherTranslator.cpp
+++ b/src/config/legacy/DispatcherTranslator.cpp
@@ -34,7 +34,7 @@ SDispatchResult CDispatcherTranslator::run(const std::string& d, const std::stri
// helper: convert ActionResult to SDispatchResult
static SDispatchResult wrap(ActionResult res) {
if (!res)
- return {.success = false, .error = res.error()};
+ return {.success = false, .error = res.error().message};
return {.passEvent = res->passEvent};
}
diff --git a/src/config/lua/ConfigManager.cpp b/src/config/lua/ConfigManager.cpp
index fad187db..7819ff2e 100644
--- a/src/config/lua/ConfigManager.cpp
+++ b/src/config/lua/ConfigManager.cpp
@@ -631,11 +631,20 @@ void CConfigManager::addError(std::string&& str) {
Notification::overlay()->addNotification(std::format("Runtime error in lua:\n{}", std::move(str)), 0, 5000, ICON_WARNING);
}
+void CConfigManager::addEvalIssue(const Config::SConfigError& err) {
+ if (!m_isEvaluating)
+ return;
+
+ if (err.level == eConfigErrorLevel::WARNING || err.level == eConfigErrorLevel::INFO)
+ m_evalIssues.emplace_back(err);
+}
+
std::optional<std::string> CConfigManager::eval(const std::string& code) {
if (!m_lua)
- return "lua state not initialized";
+ return "error: lua state not initialized";
m_errors.clear();
+ m_evalIssues.clear();
m_isEvaluating = true;
Hyprutils::Utils::CScopeGuard x([this] { m_isEvaluating = false; });
@@ -643,30 +652,37 @@ std::optional<std::string> CConfigManager::eval(const std::string& code) {
if (luaL_loadstring(m_lua, code.c_str()) != LUA_OK) {
std::string err = lua_tostring(m_lua, -1);
lua_pop(m_lua, 1);
- return err;
+ return std::format("error: {}", err);
}
if (guardedPCall(0, 0, 0, LUA_TIMEOUT_EVAL_MS, "hyprctl eval") != LUA_OK) {
std::string err = lua_tostring(m_lua, -1);
lua_pop(m_lua, 1);
- return err;
+ return std::format("error: {}", err);
}
- if (!m_errors.empty()) {
+ if (!m_errors.empty() || !m_evalIssues.empty()) {
std::string out;
out.reserve(256);
for (size_t i = 0; i < m_errors.size(); ++i) {
+ out += "error: ";
out += m_errors.at(i);
out += "\n";
}
+ for (const auto& issue : m_evalIssues) {
+ out += std::format("{}: {}", Config::toString(issue.level), issue.message);
+ out += "\n";
+ }
+
out.pop_back();
return out;
}
m_errors.clear();
+ m_evalIssues.clear();
return std::nullopt;
}
diff --git a/src/config/lua/ConfigManager.hpp b/src/config/lua/ConfigManager.hpp
index 2f9827d4..77387758 100644
--- a/src/config/lua/ConfigManager.hpp
+++ b/src/config/lua/ConfigManager.hpp
@@ -21,6 +21,7 @@
#include "../../SharedDefs.hpp"
#include "../../managers/KeybindManager.hpp"
+#include "../shared/ConfigErrors.hpp"
extern "C" {
#include <lua.h>
@@ -82,6 +83,7 @@ namespace Config::Lua {
std::expected<void, std::string> unregisterPluginLuaFunction(void* handle, const std::string& namespace_, const std::string& name);
void addError(std::string&& str);
+ void addEvalIssue(const Config::SConfigError& err);
void registerLuaRef(int ref);
void callLuaFn(int ref);
@@ -127,6 +129,7 @@ namespace Config::Lua {
std::unordered_map<std::string, SDeviceConfig> m_deviceConfigs;
std::vector<std::string> m_errors, m_configPaths;
+ std::vector<Config::SConfigError> m_evalIssues;
// named window/layer rules for merge-on-redeclaration
std::unordered_map<std::string, SP<Desktop::Rule::CWindowRule>> m_luaWindowRules;
diff --git a/src/config/lua/bindings/LuaBindingsDispatchers.cpp b/src/config/lua/bindings/LuaBindingsDispatchers.cpp
index 737562c6..fc84895a 100644
--- a/src/config/lua/bindings/LuaBindingsDispatchers.cpp
+++ b/src/config/lua/bindings/LuaBindingsDispatchers.cpp
@@ -15,44 +15,46 @@ using namespace Hyprutils::String;
namespace CA = Config::Actions;
-static int dsp_moveCursorToCorner(lua_State* L) {
- Internal::checkResult(L, CA::moveCursorToCorner((int)lua_tonumber(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
- return 0;
+static constexpr auto ERR = CA::eActionErrorLevel::ERROR;
+static constexpr auto WARN = CA::eActionErrorLevel::WARNING;
+static constexpr auto INFO = CA::eActionErrorLevel::INFO;
+static constexpr auto C_UNKNOWN = CA::eActionErrorCode::UNKNOWN;
+static constexpr auto C_INVARG = CA::eActionErrorCode::INVALID_ARGUMENT;
+static constexpr auto C_NOTFOUND = CA::eActionErrorCode::NOT_FOUND;
+static constexpr auto C_NOTARGET = CA::eActionErrorCode::NO_TARGET;
+static constexpr auto C_UNAVAIL = CA::eActionErrorCode::UNAVAILABLE;
+static constexpr auto C_EXECFAIL = CA::eActionErrorCode::EXECUTION_FAILED;
+
+static int dsp_moveCursorToCorner(lua_State* L) {
+ return Internal::checkResult(L, CA::moveCursorToCorner((int)lua_tonumber(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
}
static int dsp_moveCursor(lua_State* L) {
- Internal::checkResult(L, CA::moveCursor(Vector2D{lua_tonumber(L, lua_upvalueindex(1)), lua_tonumber(L, lua_upvalueindex(2))}));
- return 0;
+ return Internal::checkResult(L, CA::moveCursor(Vector2D{lua_tonumber(L, lua_upvalueindex(1)), lua_tonumber(L, lua_upvalueindex(2))}));
}
static int dsp_toggleGroup(lua_State* L) {
- Internal::checkResult(L, CA::toggleGroup(Internal::windowFromUpval(L, 1)));
- return 0;
+ return Internal::checkResult(L, CA::toggleGroup(Internal::windowFromUpval(L, 1)));
}
static int dsp_changeGroupActive(lua_State* L) {
- Internal::checkResult(L, CA::changeGroupActive(lua_toboolean(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::changeGroupActive(lua_toboolean(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
}
static int dsp_setGroupActive(lua_State* L) {
- Internal::checkResult(L, CA::setGroupActive((int)lua_tonumber(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::setGroupActive((int)lua_tonumber(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
}
static int dsp_moveGroupWindow(lua_State* L) {
- Internal::checkResult(L, CA::moveGroupWindow(lua_toboolean(L, lua_upvalueindex(1))));
- return 0;
+ return Internal::checkResult(L, CA::moveGroupWindow(lua_toboolean(L, lua_upvalueindex(1))));
}
static int dsp_lockGroups(lua_State* L) {
- Internal::checkResult(L, CA::lockGroups(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1)))));
- return 0;
+ return Internal::checkResult(L, CA::lockGroups(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1)))));
}
static int dsp_lockActiveGroup(lua_State* L) {
- Internal::checkResult(L, CA::lockActiveGroup(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1)))));
- return 0;
+ return Internal::checkResult(L, CA::lockActiveGroup(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1)))));
}
static int hlCursorMoveToCorner(lua_State* L) {
@@ -137,7 +139,7 @@ static int dsp_execCmd(lua_State* L) {
auto proc = lua_tostring(L, lua_upvalueindex(1));
if (std::string_view{proc}.empty())
- return Internal::configError(L, "Invalid process string");
+ return Internal::dispatcherError(L, "Invalid process string", ERR, C_INVARG);
std::optional<uint64_t> pid;
auto ruleRet = Internal::buildRuleFromTable(L, lua_upvalueindex(2));
@@ -151,38 +153,34 @@ static int dsp_execCmd(lua_State* L) {
pid = Config::Supplementary::executor()->spawn(proc);
if (!pid.has_value())
- return Internal::configError(L, "Failed to start process");
- return 0;
+ return Internal::dispatcherError(L, "Failed to start process", ERR, C_EXECFAIL);
+ return Internal::pushSuccessResult(L);
}
static int dsp_execRaw(lua_State* L) {
auto proc = Config::Supplementary::executor()->spawnRaw(lua_tostring(L, lua_upvalueindex(1)));
if (!proc || !*proc)
- return Internal::configError(L, "Failed to start process");
- return 0;
+ return Internal::dispatcherError(L, "Failed to start process", ERR, C_EXECFAIL);
+ return Internal::pushSuccessResult(L);
}
static int dsp_exit(lua_State* L) {
- Internal::checkResult(L, CA::exit());
- return 0;
+ return Internal::checkResult(L, CA::exit());
}
static int dsp_submap(lua_State* L) {
- Internal::checkResult(L, CA::setSubmap(lua_tostring(L, lua_upvalueindex(1))));
- return 0;
+ return Internal::checkResult(L, CA::setSubmap(lua_tostring(L, lua_upvalueindex(1))));
}
static int dsp_pass(lua_State* L) {
const auto PWINDOW = g_pCompositor->getWindowByRegex(lua_tostring(L, lua_upvalueindex(1)));
if (!PWINDOW)
- return Internal::configError(L, "hl.pass: window not found");
- Internal::checkResult(L, CA::pass(PWINDOW));
- return 0;
+ return Internal::dispatcherError(L, "hl.pass: window not found", WARN, C_NOTFOUND);
+ return Internal::checkResult(L, CA::pass(PWINDOW));
}
static int dsp_layoutMsg(lua_State* L) {
- Internal::checkResult(L, CA::layoutMessage(lua_tostring(L, lua_upvalueindex(1))));
- return 0;
+ return Internal::checkResult(L, CA::layoutMessage(lua_tostring(L, lua_upvalueindex(1))));
}
static int dsp_dpms(lua_State* L) {
@@ -195,28 +193,23 @@ static int dsp_dpms(lua_State* L) {
mon = m;
}
- Internal::checkResult(L, CA::dpms(action, mon));
- return 0;
+ return Internal::checkResult(L, CA::dpms(action, mon));
}
static int dsp_event(lua_State* L) {
- Internal::checkResult(L, CA::event(lua_tostring(L, lua_upvalueindex(1))));
- return 0;
+ return Internal::checkResult(L, CA::event(lua_tostring(L, lua_upvalueindex(1))));
}
static int dsp_global(lua_State* L) {
- Internal::checkResult(L, CA::global(lua_tostring(L, lua_upvalueindex(1))));
- return 0;
+ return Internal::checkResult(L, CA::global(lua_tostring(L, lua_upvalueindex(1))));
}
static int dsp_forceRendererReload(lua_State* L) {
- Internal::checkResult(L, CA::forceRendererReload());
- return 0;
+ return Internal::checkResult(L, CA::forceRendererReload());
}
static int dsp_forceIdle(lua_State* L) {
- Internal::checkResult(L, CA::forceIdle((float)lua_tonumber(L, lua_upvalueindex(1))));
- return 0;
+ return Internal::checkResult(L, CA::forceIdle((float)lua_tonumber(L, lua_upvalueindex(1))));
}
static int hlExecCmd(lua_State* L) {
@@ -357,17 +350,16 @@ static int dsp_sendShortcut(lua_State* L) {
auto keycodeResult = resolveKeycode(key);
if (!keycodeResult)
- return Internal::configError(L, "send_shortcut: {}", keycodeResult.error());
+ return Internal::dispatcherError(L, std::format("send_shortcut: {}", keycodeResult.error()), ERR, C_INVARG);
PHLWINDOW window = nullptr;
if (!lua_isnil(L, lua_upvalueindex(3))) {
window = g_pCompositor->getWindowByRegex(lua_tostring(L, lua_upvalueindex(3)));
if (!window)
- return Internal::configError(L, "send_shortcut: window not found");
+ return Internal::dispatcherError(L, "send_shortcut: window not found", WARN, C_NOTFOUND);
}
- Internal::checkResult(L, CA::pass(modMask, *keycodeResult, window));
- return 0;
+ return Internal::checkResult(L, CA::pass(modMask, *keycodeResult, window));
}
static int dsp_sendKeyState(lua_State* L) {
@@ -377,17 +369,16 @@ static int dsp_sendKeyState(lua_State* L) {
auto keycodeResult = resolveKeycode(key);
if (!keycodeResult)
- return Internal::configError(L, "send_key_state: {}", keycodeResult.error());
+ return Internal::dispatcherError(L, std::format("send_key_state: {}", keycodeResult.error()), ERR, C_INVARG);
PHLWINDOW window = nullptr;
if (!lua_isnil(L, lua_upvalueindex(4))) {
window = g_pCompositor->getWindowByRegex(lua_tostring(L, lua_upvalueindex(4)));
if (!window)
- return Internal::configError(L, "send_key_state: window not found");
+ return Internal::dispatcherError(L, "send_key_state: window not found", WARN, C_NOTFOUND);
}
- Internal::checkResult(L, CA::sendKeyState(modMask, *keycodeResult, keyState, window));
- return 0;
+ return Internal::checkResult(L, CA::sendKeyState(modMask, *keycodeResult, keyState, window));
}
static int hlSendShortcut(lua_State* L) {
@@ -431,46 +422,39 @@ static int hlSendKeyState(lua_State* L) {
static int dsp_moveToWorkspace(lua_State* L) {
auto ws = Internal::resolveWorkspaceStr(lua_tostring(L, lua_upvalueindex(1)));
if (!ws)
- return Internal::configError(L, "Invalid workspace");
+ return Internal::dispatcherError(L, "Invalid workspace", ERR, C_INVARG);
bool silent = lua_toboolean(L, lua_upvalueindex(2));
- Internal::checkResult(L, CA::moveToWorkspace(ws, silent, Internal::windowFromUpval(L, 3)));
- return 0;
+ return Internal::checkResult(L, CA::moveToWorkspace(ws, silent, Internal::windowFromUpval(L, 3)));
}
static int dsp_moveToMonitor(lua_State* L) {
auto mon = Internal::resolveMonitorStr(lua_tostring(L, lua_upvalueindex(1)));
if (!mon)
- return Internal::configError(L, "Invalid monitor / monitor doesn't exist");
+ return Internal::dispatcherError(L, "Invalid monitor / monitor doesn't exist", ERR, C_INVARG);
bool silent = lua_toboolean(L, lua_upvalueindex(2));
- Internal::checkResult(L, CA::moveToWorkspace(mon->m_activeWorkspace, silent, Internal::windowFromUpval(L, 3)));
- return 0;
+ return Internal::checkResult(L, CA::moveToWorkspace(mon->m_activeWorkspace, silent, Internal::windowFromUpval(L, 3)));
}
static int dsp_closeWindow(lua_State* L) {
- Internal::checkResult(L, CA::closeWindow(Internal::windowFromUpval(L, 1)));
- return 0;
+ return Internal::checkResult(L, CA::closeWindow(Internal::windowFromUpval(L, 1)));
}
static int dsp_killWindow(lua_State* L) {
- Internal::checkResult(L, CA::killWindow(Internal::windowFromUpval(L, 1)));
- return 0;
+ return Internal::checkResult(L, CA::killWindow(Internal::windowFromUpval(L, 1)));
}
static int dsp_signalWindow(lua_State* L) {
- Internal::checkResult(L, CA::signalWindow((int)lua_tonumber(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::signalWindow((int)lua_tonumber(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
}
static int dsp_floatWindow(lua_State* L) {
- Internal::checkResult(L, CA::floatWindow(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::floatWindow(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
}
static int dsp_fullscreenWindow(lua_State* L) {
- Internal::checkResult(L, CA::fullscreenWindow(sc<eFullscreenMode>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::fullscreenWindow(sc<eFullscreenMode>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
}
static int dsp_fullscreenWindowWithAction(lua_State* L) {
@@ -479,29 +463,28 @@ static int dsp_fullscreenWindowWithAction(lua_State* L) {
auto maybeW = Internal::windowFromUpval(L, 3);
if (actionRaw == 0) {
- Internal::checkResult(L, CA::fullscreenWindow(mode, maybeW));
- return 0;
+ return Internal::checkResult(L, CA::fullscreenWindow(mode, maybeW));
}
const auto target = maybeW.value_or(Desktop::focusState()->window());
if (!target)
- return 0;
+ return Internal::dispatcherError(L, "hl.window.fullscreen: no target", WARN, C_NOTARGET);
const bool currentlyMode = target->isEffectiveInternalFSMode(mode);
if (actionRaw == 1) {
if (!currentlyMode)
- Internal::checkResult(L, CA::fullscreenWindow(mode, maybeW));
- return 0;
+ return Internal::checkResult(L, CA::fullscreenWindow(mode, maybeW));
+ return Internal::pushSuccessResult(L);
}
if (actionRaw == 2) {
if (currentlyMode)
- Internal::checkResult(L, CA::fullscreenWindow(mode, maybeW));
- return 0;
+ return Internal::checkResult(L, CA::fullscreenWindow(mode, maybeW));
+ return Internal::pushSuccessResult(L);
}
- return Internal::configError(L, "hl.window.fullscreen: invalid action");
+ return Internal::dispatcherError(L, "hl.window.fullscreen: invalid action", ERR, C_INVARG);
}
static int dsp_fullscreenState(lua_State* L) {
@@ -512,49 +495,44 @@ static int dsp_fullscreenState(lua_State* L) {
const auto target = maybeW.value_or(Desktop::focusState()->window());
if (!target)
- return 0;
+ return Internal::pushSuccessResult(L);
const auto CURRENT = target->m_fullscreenState;
const bool atDesiredState = CURRENT.internal == desiredInternal && CURRENT.client == desiredClient;
if (actionRaw == 0) {
- Internal::checkResult(L, CA::fullscreenWindow(desiredInternal, desiredClient, maybeW));
- return 0;
+ return Internal::checkResult(L, CA::fullscreenWindow(desiredInternal, desiredClient, maybeW));
}
if (actionRaw == 1) {
if (!atDesiredState)
- Internal::checkResult(L, CA::fullscreenWindow(desiredInternal, desiredClient, maybeW));
- return 0;
+ return Internal::checkResult(L, CA::fullscreenWindow(desiredInternal, desiredClient, maybeW));
+ return Internal::pushSuccessResult(L);
}
if (actionRaw == 2) {
if (atDesiredState)
- Internal::checkResult(L, CA::fullscreenWindow(desiredInternal, desiredClient, maybeW));
- return 0;
+ return Internal::checkResult(L, CA::fullscreenWindow(desiredInternal, desiredClient, maybeW));
+ return Internal::pushSuccessResult(L);
}
- return Internal::configError(L, "hl.window.fullscreen_state: invalid action");
+ return Internal::dispatcherError(L, "hl.window.fullscreen_state: invalid action", ERR, C_INVARG);
}
static int dsp_pseudoWindow(lua_State* L) {
- Internal::checkResult(L, CA::pseudoWindow(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::pseudoWindow(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
}
static int dsp_moveInDirection(lua_State* L) {
- Internal::checkResult(L, CA::moveInDirection(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::moveInDirection(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
}
static int dsp_swapInDirection(lua_State* L) {
- Internal::checkResult(L, CA::swapInDirection(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::swapInDirection(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
}
static int dsp_center(lua_State* L) {
- Internal::checkResult(L, CA::center(Internal::windowFromUpval(L, 1)));
- return 0;
+ return Internal::checkResult(L, CA::center(Internal::windowFromUpval(L, 1)));
}
static int dsp_cycleNext(lua_State* L) {
@@ -563,13 +541,11 @@ static int dsp_cycleNext(lua_State* L) {
int floatingRaw = (int)lua_tonumber(L, lua_upvalueindex(3));
std::optional<bool> tiled = tiledRaw < 0 ? std::nullopt : std::optional(tiledRaw > 0);
std::optional<bool> floating = floatingRaw < 0 ? std::nullopt : std::optional(floatingRaw > 0);
- Internal::checkResult(L, CA::cycleNext(next, tiled, floating, Internal::windowFromUpval(L, 4)));
- return 0;
+ return Internal::checkResult(L, CA::cycleNext(next, tiled, floating, Internal::windowFromUpval(L, 4)));
}
static int dsp_swapNext(lua_State* L) {
- Internal::checkResult(L, CA::swapNext(lua_toboolean(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::swapNext(lua_toboolean(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
}
static int dsp_swapWithWindow(lua_State* L) {
@@ -578,87 +554,71 @@ static int dsp_swapWithWindow(lua_State* L) {
const auto targetSelector = lua_tostring(L, lua_upvalueindex(2));
const auto target = g_pCompositor->getWindowByRegex(targetSelector);
if (!target)
- return Internal::configError(L, "hl.window.swap: target window not found");
+ return Internal::dispatcherError(L, "hl.window.swap: target window not found", WARN, C_NOTFOUND);
- Internal::checkResult(L, CA::swapWith(target, source));
- return 0;
+ return Internal::checkResult(L, CA::swapWith(target, source));
}
static int dsp_tagWindow(lua_State* L) {
- Internal::checkResult(L, CA::tag(lua_tostring(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::tag(lua_tostring(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
}
static int dsp_toggleSwallow(lua_State* L) {
- Internal::checkResult(L, CA::toggleSwallow());
- return 0;
+ return Internal::checkResult(L, CA::toggleSwallow());
}
static int dsp_resize(lua_State* L) {
Vector2D value{lua_tonumber(L, lua_upvalueindex(1)), lua_tonumber(L, lua_upvalueindex(2))};
- Internal::checkResult(L, CA::resize(value, lua_toboolean(L, lua_upvalueindex(3)), Internal::windowFromUpval(L, 4)));
- return 0;
+ return Internal::checkResult(L, CA::resize(value, lua_toboolean(L, lua_upvalueindex(3)), Internal::windowFromUpval(L, 4)));
}
static int dsp_move(lua_State* L) {
Vector2D value{lua_tonumber(L, lua_upvalueindex(1)), lua_tonumber(L, lua_upvalueindex(2))};
- Internal::checkResult(L, CA::move(value, lua_toboolean(L, lua_upvalueindex(3)), Internal::windowFromUpval(L, 4)));
- return 0;
+ return Internal::checkResult(L, CA::move(value, lua_toboolean(L, lua_upvalueindex(3)), Internal::windowFromUpval(L, 4)));
}
static int dsp_pinWindow(lua_State* L) {
- Internal::checkResult(L, CA::pinWindow(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::pinWindow(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
}
static int dsp_bringToTop(lua_State* L) {
- Internal::checkResult(L, CA::alterZOrder("top"));
- return 0;
+ return Internal::checkResult(L, CA::alterZOrder("top"));
}
static int dsp_alterZOrder(lua_State* L) {
- Internal::checkResult(L, CA::alterZOrder(lua_tostring(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::alterZOrder(lua_tostring(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
}
static int dsp_setProp(lua_State* L) {
- Internal::checkResult(L, CA::setProp(lua_tostring(L, lua_upvalueindex(1)), lua_tostring(L, lua_upvalueindex(2)), Internal::windowFromUpval(L, 3)));
- return 0;
+ return Internal::checkResult(L, CA::setProp(lua_tostring(L, lua_upvalueindex(1)), lua_tostring(L, lua_upvalueindex(2)), Internal::windowFromUpval(L, 3)));
}
static int dsp_moveIntoGroup(lua_State* L) {
- Internal::checkResult(L, CA::moveIntoGroup(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::moveIntoGroup(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
}
static int dsp_moveOutOfGroup(lua_State* L) {
- Internal::checkResult(L, CA::moveOutOfGroup(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::moveOutOfGroup(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
}
static int dsp_moveWindowOrGroup(lua_State* L) {
- Internal::checkResult(L, CA::moveWindowOrGroup(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::moveWindowOrGroup(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
}
static int dsp_moveIntoOrCreateGroup(lua_State* L) {
- Internal::checkResult(L, CA::moveIntoOrCreateGroup(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
- return 0;
+ return Internal::checkResult(L, CA::moveIntoOrCreateGroup(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2)));
}
static int dsp_denyFromGroup(lua_State* L) {
- Internal::checkResult(L, CA::denyWindowFromGroup(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1)))));
- return 0;
+ return Internal::checkResult(L, CA::denyWindowFromGroup(sc<CA::eTogglableAction>((int)lua_tonumber(L, lua_upvalueindex(1)))));
}
static int dsp_mouseDrag(lua_State* L) {
- Internal::checkResult(L, CA::mouse("movewindow"));
- return 0;
+ return Internal::checkResult(L, CA::mouse("movewindow"));
}
static int dsp_mouseResize(lua_State* L) {
- Internal::checkResult(L, CA::mouse("resizewindow"));
- return 0;
+ return Internal::checkResult(L, CA::mouse("resizewindow"));
}
static int hlWindowClose(lua_State* L) {
@@ -1040,47 +1000,40 @@ static int hlWindowResize(lua_State* L) {
}
static int dsp_moveFocus(lua_State* L) {
- Internal::checkResult(L, CA::moveFocus(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1)))));
- return 0;
+ return Internal::checkResult(L, CA::moveFocus(sc<Math::eDirection>((int)lua_tonumber(L, lua_upvalueindex(1)))));
}
static int dsp_focusMonitor(lua_State* L) {
const auto PMONITOR = g_pCompositor->getMonitorFromString(lua_tostring(L, lua_upvalueindex(1)));
if (!PMONITOR)
- return Internal::configError(L, "hl.focus.monitor: monitor not found");
- Internal::checkResult(L, CA::focusMonitor(PMONITOR));
- return 0;
+ return Internal::dispatcherError(L, "hl.focus.monitor: monitor not found", WARN, C_NOTFOUND);
+ return Internal::checkResult(L, CA::focusMonitor(PMONITOR));
}
static int dsp_focusWindowBySelector(lua_State* L) {
const auto PWINDOW = g_pCompositor->getWindowByRegex(lua_tostring(L, lua_upvalueindex(1)));
if (!PWINDOW)
- return Internal::configError(L, "hl.focus: window not found");
- Internal::checkResult(L, CA::focus(PWINDOW));
- return 0;
+ return Internal::dispatcherError(L, "hl.focus: window not found", WARN, C_NOTFOUND);
+ return Internal::checkResult(L, CA::focus(PWINDOW));
}
static int dsp_focusUrgentOrLast(lua_State* L) {
- Internal::checkResult(L, CA::focusUrgentOrLast());
- return 0;
+ return Internal::checkResult(L, CA::focusUrgentOrLast());
}
static int dsp_focusCurrentOrLast(lua_State* L) {
- Internal::checkResult(L, CA::focusCurrentOrLast());
- return 0;
+ return Internal::checkResult(L, CA::focusCurrentOrLast());
}
static int dsp_changeWorkspace(lua_State* L) {
- Internal::checkResult(L, CA::changeWorkspace(std::string(lua_tostring(L, lua_upvalueindex(1)))));
- return 0;
+ return Internal::checkResult(L, CA::changeWorkspace(std::string(lua_tostring(L, lua_upvalueindex(1)))));
}
static int dsp_focusWorkspaceOnCurrentMonitor(lua_State* L) {
auto ws = Internal::resolveWorkspaceStr(lua_tostring(L, lua_upvalueindex(1)));
if (!ws)
- return Internal::configError(L, "Invalid workspace");
- Internal::checkResult(L, CA::changeWorkspaceOnCurrentMonitor(ws));
- return 0;
+ return Internal::dispatcherError(L, "Invalid workspace", ERR, C_INVARG);
+ return Internal::checkResult(L, CA::changeWorkspaceOnCurrentMonitor(ws));
}
static int hlFocus(lua_State* L) {
@@ -1144,7 +1097,7 @@ static int dsp_toggleSpecial(lua_State* L) {
std::string name = lua_isnil(L, lua_upvalueindex(1)) ? "" : lua_tostring(L, lua_upvalueindex(1));
const auto& [workspaceID, workspaceName, isAutoID] = getWorkspaceIDNameFromString("special:" + name);
if (workspaceID == WORKSPACE_INVALID || !g_pCompositor->isWorkspaceSpecial(workspaceID))
- return Internal::configError(L, "Invalid special workspace");
+ return Internal::dispatcherError(L, "Invalid special workspace", ERR, C_INVARG);
auto ws = g_pCompositor->getWorkspaceByID(workspaceID);
if (!ws) {
@@ -1153,53 +1106,48 @@ static int dsp_toggleSpecial(lua_State* L) {
ws = g_pCompositor->createNewWorkspace(workspaceID, PMONITOR->m_id, workspaceName);
}
if (!ws)
- return Internal::configError(L, "Could not resolve special workspace");
+ return Internal::dispatcherError(L, "Could not resolve special workspace", ERR, C_UNAVAIL);
- Internal::checkResult(L, CA::toggleSpecial(ws));
- return 0;
+ return Internal::checkResult(L, CA::toggleSpecial(ws));
}
static int dsp_renameWorkspace(lua_State* L) {
const auto PWS = g_pCompositor->getWorkspaceByString(lua_tostring(L, lua_upvalueindex(1)));
if (!PWS)
- return Internal::configError(L, "hl.workspace.rename: no such workspace");
+ return Internal::dispatcherError(L, "hl.workspace.rename: no such workspace", WARN, C_NOTFOUND);
std::string name = lua_isnil(L, lua_upvalueindex(2)) ? "" : lua_tostring(L, lua_upvalueindex(2));
- Internal::checkResult(L, CA::renameWorkspace(PWS, name));
- return 0;
+ return Internal::checkResult(L, CA::renameWorkspace(PWS, name));
}
static int dsp_moveWorkspaceToMonitor(lua_State* L) {
const auto WORKSPACEID = getWorkspaceIDNameFromString(lua_tostring(L, lua_upvalueindex(1))).id;
if (WORKSPACEID == WORKSPACE_INVALID)
- return Internal::configError(L, "Invalid workspace");
+ return Internal::dispatcherError(L, "Invalid workspace", ERR, C_INVARG);
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID);
if (!PWORKSPACE)
- return Internal::configError(L, "Workspace not found");
+ return Internal::dispatcherError(L, "Workspace not found", WARN, C_NOTFOUND);
const auto PMONITOR = g_pCompositor->getMonitorFromString(lua_tostring(L, lua_upvalueindex(2)));
if (!PMONITOR)
- return Internal::configError(L, "Monitor not found");
- Internal::checkResult(L, CA::moveToMonitor(PWORKSPACE, PMONITOR));
- return 0;
+ return Internal::dispatcherError(L, "Monitor not found", WARN, C_NOTFOUND);
+ return Internal::checkResult(L, CA::moveToMonitor(PWORKSPACE, PMONITOR));
}
static int dsp_moveCurrentWorkspaceToMonitor(lua_State* L) {
const auto PMONITOR = g_pCompositor->getMonitorFromString(lua_tostring(L, lua_upvalueindex(1)));
if (!PMONITOR)
- return Internal::configError(L, "Monitor not found");
+ return Internal::dispatcherError(L, "Monitor not found", WARN, C_NOTFOUND);
const auto PCURRENTWORKSPACE = Desktop::focusState()->monitor()->m_activeWorkspace;
if (!PCURRENTWORKSPACE)
- return Internal::configError(L, "Invalid workspace");
- Internal::checkResult(L, CA::moveToMonitor(PCURRENTWORKSPACE, PMONITOR));
- return 0;
+ return Internal::dispatcherError(L, "Invalid workspace", ERR, C_INVARG);
+ return Internal::checkResult(L, CA::moveToMonitor(PCURRENTWORKSPACE, PMONITOR));
}
static int dsp_swapActiveWorkspaces(lua_State* L) {
const auto PMON1 = g_pCompositor->getMonitorFromString(lua_tostring(L, lua_upvalueindex(1)));
const auto PMON2 = g_pCompositor->getMonitorFromString(lua_tostring(L, lua_upvalueindex(2)));
if (!PMON1 || !PMON2)
- return Internal::configError(L, "Monitor not found");
- Internal::checkResult(L, CA::swapActiveWorkspaces(PMON1, PMON2));
- return 0;
+ return Internal::dispatcherError(L, "Monitor not found", WARN, C_NOTFOUND);
+ return Internal::checkResult(L, CA::swapActiveWorkspaces(PMON1, PMON2));
}
static int hlWorkspaceToggleSpecial(lua_State* L) {
diff --git a/src/config/lua/bindings/LuaBindingsInternal.cpp b/src/config/lua/bindings/LuaBindingsInternal.cpp
index 18573759..02b7c060 100644
--- a/src/config/lua/bindings/LuaBindingsInternal.cpp
+++ b/src/config/lua/bindings/LuaBindingsInternal.cpp
@@ -327,9 +327,59 @@ void Internal::pushWindowUpval(lua_State* L, int tableIdx) {
lua_pushnil(L);
}
-void Internal::checkResult(lua_State* L, const CA::ActionResult& r) {
- if (!r)
- Internal::configError(L, "{}", r.error());
+static auto logLevelForActionError(CA::eActionErrorLevel level) {
+ switch (level) {
+ case CA::eActionErrorLevel::SILENT: return Log::DEBUG;
+ case CA::eActionErrorLevel::INFO: return Log::INFO;
+ case CA::eActionErrorLevel::WARNING: return Log::WARN;
+ case CA::eActionErrorLevel::ERROR: return Log::ERR;
+ }
+
+ return Log::ERR;
+}
+
+void Internal::reportError(lua_State* L, const CA::SActionError& e) {
+ Log::logger->log(logLevelForActionError(e.level), "Lua {} ({}): {}", CA::toString(e.level), CA::toString(e.code), e.message);
+
+ if (auto mgr = Config::Lua::mgr(); mgr) {
+ mgr->addEvalIssue(e);
+
+ if (e.level == CA::eActionErrorLevel::ERROR)
+ mgr->addError(std::string{e.message});
+ }
+}
+
+int Internal::pushSuccessResult(lua_State* L, const CA::SActionResult& r) {
+ lua_newtable(L);
+ lua_pushboolean(L, true);
+ lua_setfield(L, -2, "ok");
+ lua_pushboolean(L, r.passEvent);
+ lua_setfield(L, -2, "pass_event");
+ return 1;
+}
+
+int Internal::pushErrorResult(lua_State* L, const CA::SActionError& e) {
+ lua_newtable(L);
+ lua_pushboolean(L, false);
+ lua_setfield(L, -2, "ok");
+ lua_pushstring(L, e.message.c_str());
+ lua_setfield(L, -2, "error");
+ lua_pushstring(L, CA::toString(e.level));
+ lua_setfield(L, -2, "level");
+ lua_pushstring(L, CA::toString(e.code));
+ lua_setfield(L, -2, "code");
+ return 1;
+}
+
+int Internal::checkResult(lua_State* L, const CA::ActionResult& r) {
+ if (!r) {
+ auto error = r.error();
+ error.message = std::format("{}: {}", getSourceInfo(L), std::move(error.message));
+ Internal::reportError(L, error);
+ return Internal::pushErrorResult(L, error);
+ }
+
+ return Internal::pushSuccessResult(L, *r);
}
PHLWORKSPACE Internal::resolveWorkspaceStr(const std::string& args) {
@@ -408,14 +458,21 @@ void Internal::setMgrFn(lua_State* L, CConfigManager* mgr, const char* name, lua
lua_setfield(L, -2, name);
}
-int Internal::configError(lua_State* L, std::string s, int stackLevel) {
+int Internal::configError(lua_State* L, std::string s, CA::eActionErrorLevel level, CA::eActionErrorCode code, int stackLevel) {
s = std::format("{}: {}", getSourceInfo(L, stackLevel), std::move(s));
- Log::logger->log(Log::ERR, "Error in lua: {}", std::string_view{s});
- Config::Lua::mgr()->addError(std::move(s));
+ Internal::reportError(L, CA::SActionError{std::move(s), level, code});
return 0;
}
+int Internal::dispatcherError(lua_State* L, std::string s, CA::eActionErrorLevel level, CA::eActionErrorCode code, int stackLevel) {
+ s = std::format("{}: {}", getSourceInfo(L, stackLevel), std::move(s));
+
+ CA::SActionError error{std::move(s), level, code};
+ Internal::reportError(L, error);
+ return Internal::pushErrorResult(L, error);
+}
+
std::expected<std::string, std::string> Internal::ruleValueToString(lua_State* L) {
if (lua_type(L, -1) == LUA_TBOOLEAN)
return lua_toboolean(L, -1) ? "true" : "false";
diff --git a/src/config/lua/bindings/LuaBindingsInternal.hpp b/src/config/lua/bindings/LuaBindingsInternal.hpp
index 2cd65184..fc07515f 100644
--- a/src/config/lua/bindings/LuaBindingsInternal.hpp
+++ b/src/config/lua/bindings/LuaBindingsInternal.hpp
@@ -127,7 +127,10 @@ namespace Config::Lua::Bindings::Internal {
std::optional<PHLWINDOW> windowFromUpval(lua_State* L, int idx);
void pushWindowUpval(lua_State* L, int tableIdx);
- void checkResult(lua_State* L, const Config::Actions::ActionResult& r);
+ int checkResult(lua_State* L, const Config::Actions::ActionResult& r);
+ int pushSuccessResult(lua_State* L, const Config::Actions::SActionResult& r = {});
+ int pushErrorResult(lua_State* L, const Config::Actions::SActionError& e);
+ void reportError(lua_State* L, const Config::Actions::SActionError& e);
PHLWORKSPACE resolveWorkspaceStr(const std::string& args);
PHLMONITOR resolveMonitorStr(const std::string& args);
std::string getSourceInfo(lua_State* L, int stackLevel = 1);
@@ -139,7 +142,10 @@ namespace Config::Lua::Bindings::Internal {
std::expected<std::string, std::string> ruleValueToString(lua_State* L);
std::expected<SP<Desktop::Rule::CWindowRule>, int> buildRuleFromTable(lua_State* L, int idx);
- int configError(lua_State* L, std::string s, int stackLevel = 1);
+ int configError(lua_State* L, std::string s, Config::Actions::eActionErrorLevel level = Config::Actions::eActionErrorLevel::ERROR,
+ Config::Actions::eActionErrorCode code = Config::Actions::eActionErrorCode::UNKNOWN, int stackLevel = 1);
+ int dispatcherError(lua_State* L, std::string s, Config::Actions::eActionErrorLevel level = Config::Actions::eActionErrorLevel::ERROR,
+ Config::Actions::eActionErrorCode code = Config::Actions::eActionErrorCode::UNKNOWN, int stackLevel = 1);
template <typename... Args>
int configError(lua_State* L, std::format_string<Args...> fmt, Args&&... args) {
@@ -147,8 +153,13 @@ namespace Config::Lua::Bindings::Internal {
}
template <typename... Args>
+ int dispatcherError(lua_State* L, Config::Actions::eActionErrorLevel level, Config::Actions::eActionErrorCode code, std::format_string<Args...> fmt, Args&&... args) {
+ return dispatcherError(L, std::format(fmt, std::forward<Args>(args)...), level, code);
+ }
+
+ template <typename... Args>
int configError(lua_State* L, int stackLevel, std::format_string<Args...> fmt, Args&&... args) {
- return configError(L, std::format(fmt, std::forward<Args>(args)...), stackLevel);
+ return configError(L, std::format(fmt, std::forward<Args>(args)...), Config::Actions::eActionErrorLevel::ERROR, Config::Actions::eActionErrorCode::UNKNOWN, stackLevel);
}
template <typename T, size_t N>
diff --git a/src/config/lua/bindings/LuaBindingsRegistration.cpp b/src/config/lua/bindings/LuaBindingsRegistration.cpp
index ba6804a9..0cb59364 100644
--- a/src/config/lua/bindings/LuaBindingsRegistration.cpp
+++ b/src/config/lua/bindings/LuaBindingsRegistration.cpp
@@ -26,6 +26,31 @@ static int hlPrint(lua_State* L) {
return 0;
}
+static SDispatchResult dispatchResultFromLua(lua_State* L, int idx) {
+ SDispatchResult result;
+
+ if (!lua_istable(L, idx))
+ return result;
+
+ lua_getfield(L, idx, "pass_event");
+ result.passEvent = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, idx, "ok");
+ if (lua_isboolean(L, -1))
+ result.success = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+
+ if (!result.success) {
+ lua_getfield(L, idx, "error");
+ if (lua_isstring(L, -1))
+ result.error = lua_tostring(L, -1);
+ lua_pop(L, 1);
+ }
+
+ return result;
+}
+
void Internal::registerBindingsImpl(lua_State* L, CConfigManager* mgr) {
Objects::CLuaTimer{}.setup(L);
Objects::CLuaEventSubscription{}.setup(L);
@@ -40,15 +65,21 @@ void Internal::registerBindingsImpl(lua_State* L, CConfigManager* mgr) {
int status = LUA_OK;
if (auto* mgr = CConfigManager::fromLuaState(L); mgr)
- status = mgr->guardedPCall(0, 0, 0, CConfigManager::LUA_TIMEOUT_KEYBIND_CALLBACK_MS, "keybind callback");
+ status = mgr->guardedPCall(0, 1, 0, CConfigManager::LUA_TIMEOUT_KEYBIND_CALLBACK_MS, "keybind callback");
else
- status = lua_pcall(L, 0, 0, 0);
+ status = lua_pcall(L, 0, 1, 0);
if (status != LUA_OK) {
- Config::Lua::mgr()->addError(std::format("error in keybind lambda: {}", lua_tostring(L, -1)));
+ Config::Lua::Bindings::Internal::reportError(L,
+ Config::Actions::SActionError{std::format("error in keybind lambda: {}", lua_tostring(L, -1)),
+ Config::Actions::eActionErrorLevel::ERROR, Config::Actions::eActionErrorCode::LUA_ERROR});
lua_pop(L, 1);
+ return {.success = false, .error = "lua keybind error"};
}
- return {};
+
+ auto result = dispatchResultFromLua(L, -1);
+ lua_pop(L, 1);
+ return result;
};
lua_newtable(L);
diff --git a/src/config/lua/bindings/LuaBindingsToplevel.cpp b/src/config/lua/bindings/LuaBindingsToplevel.cpp
index d099c0a8..afd9337c 100644
--- a/src/config/lua/bindings/LuaBindingsToplevel.cpp
+++ b/src/config/lua/bindings/LuaBindingsToplevel.cpp
@@ -287,17 +287,23 @@ static int hlDispatch(lua_State* L) {
lua_pushvalue(L, 1);
int status = LUA_OK;
if (auto* mgr = CConfigManager::fromLuaState(L); mgr)
- status = mgr->guardedPCall(0, 0, 0, CConfigManager::LUA_TIMEOUT_DISPATCH_MS, "hl.dispatch");
+ status = mgr->guardedPCall(0, 1, 0, CConfigManager::LUA_TIMEOUT_DISPATCH_MS, "hl.dispatch");
else
- status = lua_pcall(L, 0, 0, 0);
+ status = lua_pcall(L, 0, 1, 0);
if (status != LUA_OK) {
const char* err = lua_tostring(L, -1);
lua_pop(L, 1);
- return Internal::configError(L, "hl.dispatch: {}", err ? err : "unknown error");
+ return Internal::dispatcherError(L, std::format("hl.dispatch: {}", err ? err : "unknown error"), Config::Actions::eActionErrorLevel::ERROR,
+ Config::Actions::eActionErrorCode::LUA_ERROR);
}
- return 0;
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+ return Internal::pushSuccessResult(L);
+ }
+
+ return 1;
}
static int hlOn(lua_State* L) {
diff --git a/src/config/shared/ConfigErrors.hpp b/src/config/shared/ConfigErrors.hpp
new file mode 100644
index 00000000..b6723400
--- /dev/null
+++ b/src/config/shared/ConfigErrors.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <expected>
+#include <cstdint>
+#include <string>
+#include <utility>
+
+namespace Config {
+ enum class eConfigErrorLevel : uint8_t {
+ SILENT = 0,
+ INFO,
+ WARNING,
+ ERROR,
+ };
+
+ enum class eConfigErrorCode : uint8_t {
+ UNKNOWN = 0,
+ INVALID_ARGUMENT,
+ NOT_FOUND,
+ NO_TARGET,
+ INVALID_STATE,
+ UNAVAILABLE,
+ EXECUTION_FAILED,
+ LUA_ERROR,
+ INTERNAL,
+ };
+
+ struct SConfigError {
+ SConfigError() = default;
+ SConfigError(std::string msg, eConfigErrorLevel lvl = eConfigErrorLevel::ERROR, eConfigErrorCode c = eConfigErrorCode::UNKNOWN) :
+ message(std::move(msg)), level(lvl), code(c) {}
+ SConfigError(const char* msg) : message(msg ? msg : "") {}
+
+ std::string message;
+ eConfigErrorLevel level = eConfigErrorLevel::ERROR;
+ eConfigErrorCode code = eConfigErrorCode::UNKNOWN;
+ };
+
+ using ErrorResult = std::expected<void, SConfigError>;
+
+ inline const char* toString(eConfigErrorLevel level) {
+ switch (level) {
+ case eConfigErrorLevel::SILENT: return "silent";
+ case eConfigErrorLevel::INFO: return "info";
+ case eConfigErrorLevel::WARNING: return "warning";
+ case eConfigErrorLevel::ERROR: return "error";
+ }
+ return "unknown";
+ }
+
+ inline const char* toString(eConfigErrorCode code) {
+ switch (code) {
+ case eConfigErrorCode::UNKNOWN: return "unknown";
+ case eConfigErrorCode::INVALID_ARGUMENT: return "invalid_argument";
+ case eConfigErrorCode::NOT_FOUND: return "not_found";
+ case eConfigErrorCode::NO_TARGET: return "no_target";
+ case eConfigErrorCode::INVALID_STATE: return "invalid_state";
+ case eConfigErrorCode::UNAVAILABLE: return "unavailable";
+ case eConfigErrorCode::EXECUTION_FAILED: return "execution_failed";
+ case eConfigErrorCode::LUA_ERROR: return "lua_error";
+ case eConfigErrorCode::INTERNAL: return "internal";
+ }
+ return "unknown";
+ }
+
+ inline std::unexpected<SConfigError> configError(std::string message, eConfigErrorLevel level = eConfigErrorLevel::ERROR, eConfigErrorCode code = eConfigErrorCode::UNKNOWN) {
+ return std::unexpected(SConfigError{std::move(message), level, code});
+ }
+}
diff --git a/src/config/shared/actions/ConfigActions.cpp b/src/config/shared/actions/ConfigActions.cpp
index fb2029a5..c36a149f 100644
--- a/src/config/shared/actions/ConfigActions.cpp
+++ b/src/config/shared/actions/ConfigActions.cpp
@@ -131,7 +131,7 @@ ActionResult Actions::closeWindow(std::optional<PHLWINDOW> w) {
return {};
if (window->m_closeableSince > Time::steadyNow())
- return std::unexpected("can't close window, it's not closeable yet (noclosefor)");
+ return actionError("can't close window, it's not closeable yet (noclosefor)", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
window->sendClose();
@@ -217,7 +217,7 @@ ActionResult Actions::pinWindow(eTogglableAction action, std::optional<PHLWINDOW
return {};
if (!window->m_isFloating || window->isFullscreen())
- return {};
+ return actionError("Window does not qualify to be pinned", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
bool wantPin = false;
switch (action) {
@@ -289,7 +289,7 @@ ActionResult Actions::moveToWorkspace(PHLWORKSPACE ws, bool silent, std::optiona
return {};
if (!ws)
- return {};
+ return std::unexpected("Invalid workspace");
if (ws->m_id == window->workspaceID())
return {};
@@ -373,11 +373,11 @@ ActionResult Actions::moveFocus(Math::eDirection dir) {
static auto PNOFALLBACK = CConfigValue<Config::INTEGER>("general:no_focus_fallback");
if (*PNOFALLBACK)
- return {};
+ return actionError(std::format("Nothing to focus to in direction {}", Math::toString(dir)), eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND);
const auto PMONITOR = PLASTWINDOW->m_monitor.lock();
if (!PMONITOR)
- return {};
+ return std::unexpected("Window has no monitor");
if (dir == Math::DIRECTION_LEFT || dir == Math::DIRECTION_RIGHT) {
if (STICKS(PLASTWINDOW->m_position.x, PMONITOR->m_position.x) && STICKS(PLASTWINDOW->m_size.x, PMONITOR->m_size.x))
@@ -440,7 +440,7 @@ ActionResult Actions::moveInDirection(Math::eDirection dir, std::optional<PHLWIN
return {};
if (window->isFullscreen())
- return {};
+ return actionError("Can't move fullscreen window", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
updateRelativeCursorCoords();
@@ -456,12 +456,12 @@ ActionResult Actions::swapInDirection(Math::eDirection dir, std::optional<PHLWIN
return {};
if (window->isFullscreen())
- return {};
+ return actionError("Can't swap fullscreen window", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(window, dir);
if (!PWINDOWTOCHANGETO || PWINDOWTOCHANGETO == window)
- return {};
+ return actionError("No window to swap with in that direction", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND);
updateRelativeCursorCoords();
g_layoutManager->switchTargets(window->layoutTarget(), PWINDOWTOCHANGETO->layoutTarget(), true);
@@ -476,13 +476,13 @@ ActionResult Actions::swapWith(PHLWINDOW other, std::optional<PHLWINDOW> w) {
return {};
if (!other)
- return {};
+ return actionError("No window to swap with", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND);
if (other == window)
- return {};
+ return actionError("Can't swap a window with itself", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
if (window->isFullscreen() || other->isFullscreen())
- return {};
+ return actionError("Can't swap fullscreen window", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
updateRelativeCursorCoords();
g_layoutManager->switchTargets(window->layoutTarget(), other->layoutTarget(), true);
@@ -495,12 +495,12 @@ ActionResult Actions::focusCurrentOrLast() {
const auto& HISTORY = Desktop::History::windowTracker()->fullHistory();
if (HISTORY.size() <= 1)
- return {};
+ return actionError("History too short", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND);
const auto PWINDOWPREV = HISTORY[HISTORY.size() - 2].lock();
if (!PWINDOWPREV)
- return {};
+ return actionError("Window not found", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND);
switchToWindow(PWINDOWPREV);
@@ -513,7 +513,7 @@ ActionResult Actions::focusUrgentOrLast() {
const auto PWINDOWPREV = Desktop::focusState()->window() ? (HISTORY.size() < 2 ? nullptr : HISTORY[1].lock()) : (HISTORY.empty() ? nullptr : HISTORY[0].lock());
if (!PWINDOWURGENT && !PWINDOWPREV)
- return {};
+ return actionError("Window not found", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND);
switchToWindow(PWINDOWURGENT ? PWINDOWURGENT : PWINDOWPREV);
@@ -523,7 +523,7 @@ ActionResult Actions::focusUrgentOrLast() {
ActionResult Actions::center(std::optional<PHLWINDOW> w) {
auto window = xtract(w);
if (!window || !window->m_isFloating || window->isFullscreen())
- return {};
+ return actionError("No floating window found", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND);
const auto PMONITOR = window->m_monitor.lock();
@@ -560,7 +560,7 @@ ActionResult Actions::resize(const Vector2D& size, bool relative, std::optional<
return {};
if (window->isFullscreen())
- return {};
+ return actionError("Window is fullscreen", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
if (!relative && (size.x < 1 || size.y < 1))
return std::unexpected("Invalid size");
@@ -581,7 +581,7 @@ ActionResult Actions::move(const Vector2D& pos, bool relative, std::optional<PHL
return {};
if (window->isFullscreen())
- return {};
+ return actionError("Window is fullscreen", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
const auto delta = relative ? pos : pos - window->m_realPosition->goal();
@@ -617,7 +617,7 @@ ActionResult Actions::swapNext(const bool next, std::optional<PHLWINDOW> w) {
toSwap = g_pCompositor->getWindowCycle(window, true, std::nullopt, false, !next);
if (!toSwap)
- return {};
+ return actionError("No window to swap with", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND);
g_layoutManager->switchTargets(window->layoutTarget(), toSwap->layoutTarget(), false);
window->m_lastCycledWindow = toSwap;
@@ -851,10 +851,10 @@ ActionResult Actions::changeGroupActive(bool forward, std::optional<PHLWINDOW> w
return {};
if (!window->m_group)
- return {};
+ return actionError("Window is not in a group", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
if (window->m_group->size() == 1)
- return {};
+ return actionError("Only one window in group", eActionErrorLevel::INFO, eActionErrorCode::INVALID_STATE);
window->m_group->moveCurrent(forward);
@@ -867,15 +867,15 @@ ActionResult Actions::setGroupActive(int index, std::optional<PHLWINDOW> w) {
return {};
if (!window->m_group)
- return {};
+ return actionError("Window is not in a group", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
if (window->m_group->size() == 1)
- return {};
+ return actionError("Only one window in group", eActionErrorLevel::INFO, eActionErrorCode::INVALID_STATE);
if (index <= 0)
window->m_group->setCurrent(window->m_group->size() - 1);
else if (sc<size_t>(index) > window->m_group->size())
- return {};
+ return actionError("Index out of range", eActionErrorLevel::ERROR, eActionErrorCode::INVALID_ARGUMENT);
else
window->m_group->setCurrent(index - 1);
@@ -884,14 +884,14 @@ ActionResult Actions::setGroupActive(int index, std::optional<PHLWINDOW> w) {
ActionResult Actions::changeWorkspace(PHLWORKSPACE ws) {
if (!ws)
- return {};
+ return std::unexpected("Invalid workspace");
static auto PHIDESPECIALONWORKSPACECHANGE = CConfigValue<Config::INTEGER>("binds:hide_special_on_workspace_change");
static auto PWORKSPACECENTERON = CConfigValue<Config::INTEGER>("binds:workspace_center_on");
const auto PMONITOR = Desktop::focusState()->monitor();
if (!PMONITOR)
- return {};
+ return std::unexpected("No monitor");
if (ws->m_isSpecialWorkspace) {
PMONITOR->setSpecialWorkspace(ws);
@@ -905,7 +905,7 @@ ActionResult Actions::changeWorkspace(PHLWORKSPACE ws) {
const auto PMONITORWORKSPACEOWNER = PMONITOR == ws->m_monitor ? PMONITOR : ws->m_monitor.lock();
if (!PMONITORWORKSPACEOWNER)
- return {};
+ return std::unexpected("Workspace has no monitor");
updateRelativeCursorCoords();
@@ -1219,10 +1219,10 @@ ActionResult Actions::lockGroups(eTogglableAction action) {
ActionResult Actions::lockActiveGroup(eTogglableAction action) {
const auto PWINDOW = Desktop::focusState()->window();
if (!PWINDOW)
- return {};
+ return actionError("No window found", eActionErrorLevel::INFO, eActionErrorCode::NO_TARGET);
if (!PWINDOW->m_group)
- return {};
+ return actionError("Window not in a group", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
switch (action) {
case TOGGLE_ACTION_TOGGLE: PWINDOW->m_group->setLocked(!PWINDOW->m_group->locked()); break;
@@ -1306,14 +1306,14 @@ ActionResult Actions::moveOutOfGroup(Math::eDirection direction, std::optional<P
static auto PIGNOREGROUPLOCK = CConfigValue<Config::INTEGER>("binds:ignore_group_lock");
if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_groupsLocked)
- return {};
+ return actionError("Groups locked", eActionErrorLevel::INFO, eActionErrorCode::INVALID_STATE);
auto window = xtract(w);
if (!window)
return {};
if (!window->m_group)
- return {};
+ return actionError("Window not in a group", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
moveWindowOutOfGroupHelper(window, direction);
@@ -1323,10 +1323,10 @@ ActionResult Actions::moveOutOfGroup(Math::eDirection direction, std::optional<P
ActionResult Actions::moveGroupWindow(bool forward) {
const auto PLASTWINDOW = Desktop::focusState()->window();
if (!PLASTWINDOW)
- return {};
+ return actionError("No window found", eActionErrorLevel::INFO, eActionErrorCode::NO_TARGET);
if (!PLASTWINDOW->m_group)
- return {};
+ return actionError("Window not in a group", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE);
if (forward)
PLASTWINDOW->m_group->swapWithNext();
@@ -1406,7 +1406,7 @@ ActionResult Actions::pass(std::optional<PHLWINDOW> w) {
return {};
if (!g_pSeatManager->m_keyboard)
- return {};
+ return std::unexpected("No keyboard");
const auto& S = *Config::Actions::state();
const auto XWTOXW = window->m_isX11 && Desktop::focusState()->window() && Desktop::focusState()->window()->m_isX11;
@@ -1474,7 +1474,7 @@ ActionResult Actions::pass(uint32_t modMask, uint32_t key, std::optional<PHLWIND
if (window) {
if (!g_pSeatManager->m_keyboard)
- return {};
+ return std::unexpected("No keyboard");
if (!isMouse)
g_pSeatManager->setKeyboardFocus(window->wlSurface()->resource());
diff --git a/src/config/shared/actions/ConfigActions.hpp b/src/config/shared/actions/ConfigActions.hpp
index b9f54f50..0f16aff7 100644
--- a/src/config/shared/actions/ConfigActions.hpp
+++ b/src/config/shared/actions/ConfigActions.hpp
@@ -7,19 +7,29 @@
#include "../../../desktop/DesktopTypes.hpp"
#include "../../../desktop/Workspace.hpp"
#include "../../../helpers/math/Direction.hpp"
+#include "../ConfigErrors.hpp"
namespace Config::Actions {
struct SActionResult {
bool passEvent = false;
};
+ using eActionErrorLevel = Config::eConfigErrorLevel;
+ using eActionErrorCode = Config::eConfigErrorCode;
+ using SActionError = Config::SConfigError;
+ using Config::toString;
+
+ inline std::unexpected<SActionError> actionError(std::string message, eActionErrorLevel level = eActionErrorLevel::ERROR, eActionErrorCode code = eActionErrorCode::UNKNOWN) {
+ return Config::configError(std::move(message), level, code);
+ }
+
enum eTogglableAction : uint8_t {
TOGGLE_ACTION_TOGGLE = 0,
TOGGLE_ACTION_ENABLE,
TOGGLE_ACTION_DISABLE,
};
- using ActionResult = std::expected<SActionResult, std::string>;
+ using ActionResult = std::expected<SActionResult, SActionError>;
ActionResult closeWindow(std::optional<PHLWINDOW> window = std::nullopt /* Active */);
ActionResult killWindow(std::optional<PHLWINDOW> window = std::nullopt /* Active */);