From 98fbbafef78057c818fb02b15c27e38ad2f5b5bc Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:56:07 +0100 Subject: 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() --- src/config/lua/ConfigManager.cpp | 24 +- src/config/lua/ConfigManager.hpp | 3 + src/config/lua/bindings/LuaBindingsDispatchers.cpp | 272 +++++++++------------ src/config/lua/bindings/LuaBindingsInternal.cpp | 69 +++++- src/config/lua/bindings/LuaBindingsInternal.hpp | 17 +- .../lua/bindings/LuaBindingsRegistration.cpp | 39 ++- src/config/lua/bindings/LuaBindingsToplevel.cpp | 14 +- 7 files changed, 255 insertions(+), 183 deletions(-) (limited to 'src/config/lua') 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 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 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 @@ -82,6 +83,7 @@ namespace Config::Lua { std::expected 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 m_deviceConfigs; std::vector m_errors, m_configPaths; + std::vector m_evalIssues; // named window/layer rules for merge-on-redeclaration std::unordered_map> 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((int)lua_tonumber(L, lua_upvalueindex(1))))); - return 0; + return Internal::checkResult(L, CA::lockGroups(sc((int)lua_tonumber(L, lua_upvalueindex(1))))); } static int dsp_lockActiveGroup(lua_State* L) { - Internal::checkResult(L, CA::lockActiveGroup(sc((int)lua_tonumber(L, lua_upvalueindex(1))))); - return 0; + return Internal::checkResult(L, CA::lockActiveGroup(sc((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 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((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); - return 0; + return Internal::checkResult(L, CA::floatWindow(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); } static int dsp_fullscreenWindow(lua_State* L) { - Internal::checkResult(L, CA::fullscreenWindow(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); - return 0; + return Internal::checkResult(L, CA::fullscreenWindow(sc((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((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); - return 0; + return Internal::checkResult(L, CA::pseudoWindow(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); } static int dsp_moveInDirection(lua_State* L) { - Internal::checkResult(L, CA::moveInDirection(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); - return 0; + return Internal::checkResult(L, CA::moveInDirection(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); } static int dsp_swapInDirection(lua_State* L) { - Internal::checkResult(L, CA::swapInDirection(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); - return 0; + return Internal::checkResult(L, CA::swapInDirection(sc((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 tiled = tiledRaw < 0 ? std::nullopt : std::optional(tiledRaw > 0); std::optional 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((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); - return 0; + return Internal::checkResult(L, CA::pinWindow(sc((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((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); - return 0; + return Internal::checkResult(L, CA::moveIntoGroup(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); } static int dsp_moveOutOfGroup(lua_State* L) { - Internal::checkResult(L, CA::moveOutOfGroup(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); - return 0; + return Internal::checkResult(L, CA::moveOutOfGroup(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); } static int dsp_moveWindowOrGroup(lua_State* L) { - Internal::checkResult(L, CA::moveWindowOrGroup(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); - return 0; + return Internal::checkResult(L, CA::moveWindowOrGroup(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); } static int dsp_moveIntoOrCreateGroup(lua_State* L) { - Internal::checkResult(L, CA::moveIntoOrCreateGroup(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); - return 0; + return Internal::checkResult(L, CA::moveIntoOrCreateGroup(sc((int)lua_tonumber(L, lua_upvalueindex(1))), Internal::windowFromUpval(L, 2))); } static int dsp_denyFromGroup(lua_State* L) { - Internal::checkResult(L, CA::denyWindowFromGroup(sc((int)lua_tonumber(L, lua_upvalueindex(1))))); - return 0; + return Internal::checkResult(L, CA::denyWindowFromGroup(sc((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((int)lua_tonumber(L, lua_upvalueindex(1))))); - return 0; + return Internal::checkResult(L, CA::moveFocus(sc((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 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 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,16 +142,24 @@ namespace Config::Lua::Bindings::Internal { std::expected ruleValueToString(lua_State* L); std::expected, 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 int configError(lua_State* L, std::format_string fmt, Args&&... args) { return configError(L, std::format(fmt, std::forward(args)...)); } + template + int dispatcherError(lua_State* L, Config::Actions::eActionErrorLevel level, Config::Actions::eActionErrorCode code, std::format_string fmt, Args&&... args) { + return dispatcherError(L, std::format(fmt, std::forward(args)...), level, code); + } + template int configError(lua_State* L, int stackLevel, std::format_string fmt, Args&&... args) { - return configError(L, std::format(fmt, std::forward(args)...), stackLevel); + return configError(L, std::format(fmt, std::forward(args)...), Config::Actions::eActionErrorLevel::ERROR, Config::Actions::eActionErrorCode::UNKNOWN, stackLevel); } template 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) { -- cgit v1.2.3