mirror of
https://github.com/hyprwm/Hyprland
synced 2026-05-07 06:58:04 +02:00
config/lua: fix dispatcher shapes to not be callable (#14268)
this would only lead to abuse, explicitly forbid it
This commit is contained in:
parent
c0933bffcf
commit
21fa9b2ee2
7 changed files with 215 additions and 62 deletions
|
|
@ -469,7 +469,8 @@ def generate_stub(root: Path) -> str:
|
|||
|
||||
api_signatures: dict[str, str] = {
|
||||
"hl.on": "fun(event: HL.EventName, cb: fun(...)): HL.EventSubscription",
|
||||
"hl.bind": "fun(keys: string, dispatcher: function, opts?: HL.BindOptions): HL.Keybind",
|
||||
"hl.bind": "fun(keys: string, dispatcher: HL.Dispatcher|function, opts?: HL.BindOptions): HL.Keybind",
|
||||
"hl.dispatch": "fun(dispatcher: HL.Dispatcher|function): any",
|
||||
"hl.define_submap": "fun(name: string, reset_or_fn: string|function, fn?: function): nil",
|
||||
"hl.timer": "fun(callback: function, opts: HL.TimerOptions): HL.Timer",
|
||||
"hl.config": "fun(config: table): nil",
|
||||
|
|
@ -523,6 +524,9 @@ def generate_stub(root: Path) -> str:
|
|||
lines.append("---@alias HL.CssGap integer|{top?:integer, right?:integer, bottom?:integer, left?:integer}")
|
||||
lines.append("---@alias HL.Gradient string|{colors:string[], angle?:number}")
|
||||
lines.append("")
|
||||
lines.append("---@class HL.Dispatcher")
|
||||
lines.append("local __HL_Dispatcher = {}")
|
||||
lines.append("")
|
||||
|
||||
lines.extend(
|
||||
emit_class_block(
|
||||
|
|
@ -651,7 +655,8 @@ def generate_stub(root: Path) -> str:
|
|||
|
||||
for method in sorted(node.methods):
|
||||
full_name = f"{full_prefix}.{method}"
|
||||
method_type = api_signatures.get(full_name, "fun(...): any")
|
||||
default_method_type = "fun(...): HL.Dispatcher" if path and path[0] == "dsp" else "fun(...): any"
|
||||
method_type = api_signatures.get(full_name, default_method_type)
|
||||
fields.append((method, method_type, False))
|
||||
|
||||
for child_name in sorted(node.children.keys()):
|
||||
|
|
|
|||
103
meta/hl.meta.lua
103
meta/hl.meta.lua
|
|
@ -378,6 +378,9 @@
|
|||
---@alias HL.CssGap integer|{top?:integer, right?:integer, bottom?:integer, left?:integer}
|
||||
---@alias HL.Gradient string|{colors:string[], angle?:number}
|
||||
|
||||
---@class HL.Dispatcher
|
||||
local __HL_Dispatcher = {}
|
||||
|
||||
---@class HL.Vec2
|
||||
---@field x number
|
||||
---@field y number
|
||||
|
|
@ -739,12 +742,12 @@ local __HL_Workspace = {}
|
|||
|
||||
---@class HL.API
|
||||
---@field animation fun(...): any
|
||||
---@field bind fun(keys: string, dispatcher: function, opts?: HL.BindOptions): HL.Keybind
|
||||
---@field bind fun(keys: string, dispatcher: HL.Dispatcher|function, opts?: HL.BindOptions): HL.Keybind
|
||||
---@field config fun(config: table): nil
|
||||
---@field curve fun(...): any
|
||||
---@field define_submap fun(name: string, reset_or_fn: string|function, fn?: function): nil
|
||||
---@field device fun(spec: HL.DeviceSpec): nil
|
||||
---@field dispatch fun(...): any
|
||||
---@field dispatch fun(dispatcher: HL.Dispatcher|function): any
|
||||
---@field env fun(...): any
|
||||
---@field exec_cmd fun(cmd: string, rules?: table<string, string|number|boolean>): nil
|
||||
---@field gesture fun(spec: HL.GestureSpec): nil
|
||||
|
|
@ -783,21 +786,21 @@ local __HL_Workspace = {}
|
|||
local __HL_API = {}
|
||||
|
||||
---@class HL.DspNamespace
|
||||
---@field dpms fun(...): any
|
||||
---@field event fun(...): any
|
||||
---@field exec_cmd fun(...): any
|
||||
---@field exec_raw fun(...): any
|
||||
---@field exit fun(...): any
|
||||
---@field focus fun(...): any
|
||||
---@field force_idle fun(...): any
|
||||
---@field force_renderer_reload fun(...): any
|
||||
---@field global fun(...): any
|
||||
---@field layout fun(...): any
|
||||
---@field no_op fun(...): any
|
||||
---@field pass fun(...): any
|
||||
---@field send_key_state fun(...): any
|
||||
---@field send_shortcut fun(...): any
|
||||
---@field submap fun(...): any
|
||||
---@field dpms fun(...): HL.Dispatcher
|
||||
---@field event fun(...): HL.Dispatcher
|
||||
---@field exec_cmd fun(...): HL.Dispatcher
|
||||
---@field exec_raw fun(...): HL.Dispatcher
|
||||
---@field exit fun(...): HL.Dispatcher
|
||||
---@field focus fun(...): HL.Dispatcher
|
||||
---@field force_idle fun(...): HL.Dispatcher
|
||||
---@field force_renderer_reload fun(...): HL.Dispatcher
|
||||
---@field global fun(...): HL.Dispatcher
|
||||
---@field layout fun(...): HL.Dispatcher
|
||||
---@field no_op fun(...): HL.Dispatcher
|
||||
---@field pass fun(...): HL.Dispatcher
|
||||
---@field send_key_state fun(...): HL.Dispatcher
|
||||
---@field send_shortcut fun(...): HL.Dispatcher
|
||||
---@field submap fun(...): HL.Dispatcher
|
||||
---@field cursor HL.DspCursorNamespace
|
||||
---@field group HL.DspGroupNamespace
|
||||
---@field window HL.DspWindowNamespace
|
||||
|
|
@ -805,48 +808,48 @@ local __HL_API = {}
|
|||
local __HL_DspNamespace = {}
|
||||
|
||||
---@class HL.DspCursorNamespace
|
||||
---@field move fun(...): any
|
||||
---@field move_to_corner fun(...): any
|
||||
---@field move fun(...): HL.Dispatcher
|
||||
---@field move_to_corner fun(...): HL.Dispatcher
|
||||
local __HL_DspCursorNamespace = {}
|
||||
|
||||
---@class HL.DspGroupNamespace
|
||||
---@field active fun(...): any
|
||||
---@field lock fun(...): any
|
||||
---@field lock_active fun(...): any
|
||||
---@field move_window fun(...): any
|
||||
---@field next fun(...): any
|
||||
---@field prev fun(...): any
|
||||
---@field toggle fun(...): any
|
||||
---@field active fun(...): HL.Dispatcher
|
||||
---@field lock fun(...): HL.Dispatcher
|
||||
---@field lock_active fun(...): HL.Dispatcher
|
||||
---@field move_window fun(...): HL.Dispatcher
|
||||
---@field next fun(...): HL.Dispatcher
|
||||
---@field prev fun(...): HL.Dispatcher
|
||||
---@field toggle fun(...): HL.Dispatcher
|
||||
local __HL_DspGroupNamespace = {}
|
||||
|
||||
---@class HL.DspWindowNamespace
|
||||
---@field alter_zorder fun(...): any
|
||||
---@field bring_to_top fun(...): any
|
||||
---@field center fun(...): any
|
||||
---@field close fun(...): any
|
||||
---@field cycle_next fun(...): any
|
||||
---@field deny_from_group fun(...): any
|
||||
---@field drag fun(...): any
|
||||
---@field float fun(...): any
|
||||
---@field fullscreen fun(...): any
|
||||
---@field fullscreen_state fun(...): any
|
||||
---@field kill fun(...): any
|
||||
---@field move fun(...): any
|
||||
---@field pin fun(...): any
|
||||
---@field pseudo fun(...): any
|
||||
---@field resize fun(...): any
|
||||
---@field set_prop fun(...): any
|
||||
---@field signal fun(...): any
|
||||
---@field swap fun(...): any
|
||||
---@field tag fun(...): any
|
||||
---@field toggle_swallow fun(...): any
|
||||
---@field alter_zorder fun(...): HL.Dispatcher
|
||||
---@field bring_to_top fun(...): HL.Dispatcher
|
||||
---@field center fun(...): HL.Dispatcher
|
||||
---@field close fun(...): HL.Dispatcher
|
||||
---@field cycle_next fun(...): HL.Dispatcher
|
||||
---@field deny_from_group fun(...): HL.Dispatcher
|
||||
---@field drag fun(...): HL.Dispatcher
|
||||
---@field float fun(...): HL.Dispatcher
|
||||
---@field fullscreen fun(...): HL.Dispatcher
|
||||
---@field fullscreen_state fun(...): HL.Dispatcher
|
||||
---@field kill fun(...): HL.Dispatcher
|
||||
---@field move fun(...): HL.Dispatcher
|
||||
---@field pin fun(...): HL.Dispatcher
|
||||
---@field pseudo fun(...): HL.Dispatcher
|
||||
---@field resize fun(...): HL.Dispatcher
|
||||
---@field set_prop fun(...): HL.Dispatcher
|
||||
---@field signal fun(...): HL.Dispatcher
|
||||
---@field swap fun(...): HL.Dispatcher
|
||||
---@field tag fun(...): HL.Dispatcher
|
||||
---@field toggle_swallow fun(...): HL.Dispatcher
|
||||
local __HL_DspWindowNamespace = {}
|
||||
|
||||
---@class HL.DspWorkspaceNamespace
|
||||
---@field move fun(...): any
|
||||
---@field rename fun(...): any
|
||||
---@field swap_monitors fun(...): any
|
||||
---@field toggle_special fun(...): any
|
||||
---@field move fun(...): HL.Dispatcher
|
||||
---@field rename fun(...): HL.Dispatcher
|
||||
---@field swap_monitors fun(...): HL.Dispatcher
|
||||
---@field toggle_special fun(...): HL.Dispatcher
|
||||
local __HL_DspWorkspaceNamespace = {}
|
||||
|
||||
---@class HL.NotificationNamespace
|
||||
|
|
|
|||
144
src/config/lua/bindings/LuaBindingsDispatcherUtils.cpp
Normal file
144
src/config/lua/bindings/LuaBindingsDispatcherUtils.cpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
#include "LuaBindingsInternal.hpp"
|
||||
|
||||
using namespace Config::Lua::Bindings;
|
||||
|
||||
static constexpr const char* DISPATCHER_MT = "HL.Dispatcher";
|
||||
static char DISPATCHER_TABLES_REGISTRY_KEY;
|
||||
|
||||
namespace {
|
||||
struct SDispatcherRef {
|
||||
int ref = LUA_NOREF;
|
||||
};
|
||||
}
|
||||
|
||||
static int dispatcherGc(lua_State* L) {
|
||||
auto* dispatcher = sc<SDispatcherRef*>(luaL_checkudata(L, 1, DISPATCHER_MT));
|
||||
if (dispatcher->ref != LUA_NOREF) {
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, dispatcher->ref);
|
||||
dispatcher->ref = LUA_NOREF;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispatcherCall(lua_State* L) {
|
||||
return Internal::configError(L, "dispatcher objects cannot be called directly; use hl.dispatch(dispatcher)");
|
||||
}
|
||||
|
||||
static int dispatcherToString(lua_State* L) {
|
||||
lua_pushstring(L, "HL.Dispatcher");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ensureDispatcherMetatable(lua_State* L) {
|
||||
if (luaL_newmetatable(L, DISPATCHER_MT)) {
|
||||
lua_pushcfunction(L, dispatcherGc);
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_pushcfunction(L, dispatcherCall);
|
||||
lua_setfield(L, -2, "__call");
|
||||
lua_pushcfunction(L, dispatcherToString);
|
||||
lua_setfield(L, -2, "__tostring");
|
||||
|
||||
lua_pushstring(L, DISPATCHER_MT);
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
static bool isDispatcherTable(lua_State* L, int idx) {
|
||||
if (!lua_istable(L, idx))
|
||||
return false;
|
||||
|
||||
idx = lua_absindex(L, idx);
|
||||
lua_pushlightuserdata(L, &DISPATCHER_TABLES_REGISTRY_KEY);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_pushvalue(L, idx);
|
||||
lua_rawget(L, -2);
|
||||
const bool result = lua_toboolean(L, -1);
|
||||
lua_pop(L, 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int dispatcherFactory(lua_State* L) {
|
||||
const int nargs = lua_gettop(L);
|
||||
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_insert(L, 1);
|
||||
lua_call(L, nargs, LUA_MULTRET);
|
||||
|
||||
const int nresults = lua_gettop(L);
|
||||
if (nresults == 1 && lua_isfunction(L, -1))
|
||||
return Internal::wrapDispatcher(L);
|
||||
|
||||
return nresults;
|
||||
}
|
||||
|
||||
void Internal::setFn(lua_State* L, const char* name, lua_CFunction fn) {
|
||||
if (isDispatcherTable(L, -1)) {
|
||||
lua_pushcfunction(L, fn);
|
||||
lua_pushcclosure(L, dispatcherFactory, 1);
|
||||
} else
|
||||
lua_pushcfunction(L, fn);
|
||||
|
||||
lua_setfield(L, -2, name);
|
||||
}
|
||||
|
||||
void Internal::markDispatcherTable(lua_State* L) {
|
||||
if (!lua_istable(L, -1))
|
||||
return;
|
||||
|
||||
lua_pushlightuserdata(L, &DISPATCHER_TABLES_REGISTRY_KEY);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
lua_newtable(L);
|
||||
lua_pushlightuserdata(L, &DISPATCHER_TABLES_REGISTRY_KEY);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
lua_pushvalue(L, -2);
|
||||
lua_pushboolean(L, true);
|
||||
lua_rawset(L, -3);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
int Internal::wrapDispatcher(lua_State* L) {
|
||||
luaL_checktype(L, -1, LUA_TFUNCTION);
|
||||
|
||||
const int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
|
||||
new (lua_newuserdata(L, sizeof(SDispatcherRef))) SDispatcherRef{.ref = ref};
|
||||
|
||||
ensureDispatcherMetatable(L);
|
||||
luaL_getmetatable(L, DISPATCHER_MT);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool Internal::pushDispatcherFunction(lua_State* L, int idx) {
|
||||
if (lua_isfunction(L, idx)) {
|
||||
lua_pushvalue(L, idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* dispatcher = sc<SDispatcherRef*>(luaL_testudata(L, idx, DISPATCHER_MT));
|
||||
if (!dispatcher || dispatcher->ref == LUA_NOREF)
|
||||
return false;
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, dispatcher->ref);
|
||||
if (lua_isfunction(L, -1))
|
||||
return true;
|
||||
|
||||
lua_pop(L, 1);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1214,14 +1214,17 @@ static int hlWorkspaceSwapMonitors(lua_State* L) {
|
|||
|
||||
void Internal::registerDispatcherBindings(lua_State* L) {
|
||||
lua_newtable(L);
|
||||
Internal::markDispatcherTable(L);
|
||||
|
||||
{
|
||||
lua_newtable(L);
|
||||
Internal::markDispatcherTable(L);
|
||||
Internal::setFn(L, "move_to_corner", hlCursorMoveToCorner);
|
||||
Internal::setFn(L, "move", hlCursorMove);
|
||||
lua_setfield(L, -2, "cursor");
|
||||
|
||||
lua_newtable(L);
|
||||
Internal::markDispatcherTable(L);
|
||||
Internal::setFn(L, "toggle", hlGroupToggle);
|
||||
Internal::setFn(L, "next", hlGroupNext);
|
||||
Internal::setFn(L, "prev", hlGroupPrev);
|
||||
|
|
@ -1232,6 +1235,7 @@ void Internal::registerDispatcherBindings(lua_State* L) {
|
|||
lua_setfield(L, -2, "group");
|
||||
|
||||
lua_newtable(L);
|
||||
Internal::markDispatcherTable(L);
|
||||
Internal::setFn(L, "close", hlWindowClose);
|
||||
Internal::setFn(L, "kill", hlWindowKill);
|
||||
Internal::setFn(L, "signal", hlWindowSignal);
|
||||
|
|
@ -1255,6 +1259,7 @@ void Internal::registerDispatcherBindings(lua_State* L) {
|
|||
lua_setfield(L, -2, "window");
|
||||
|
||||
lua_newtable(L);
|
||||
Internal::markDispatcherTable(L);
|
||||
Internal::setFn(L, "rename", hlWorkspaceRename);
|
||||
Internal::setFn(L, "move", hlWorkspaceMove);
|
||||
Internal::setFn(L, "swap_monitors", hlWorkspaceSwapMonitors);
|
||||
|
|
|
|||
|
|
@ -449,11 +449,6 @@ CA::eTogglableAction Internal::tableToggleAction(lua_State* L, int idx, const ch
|
|||
return CA::TOGGLE_ACTION_TOGGLE;
|
||||
}
|
||||
|
||||
void Internal::setFn(lua_State* L, const char* name, lua_CFunction fn) {
|
||||
lua_pushcfunction(L, fn);
|
||||
lua_setfield(L, -2, name);
|
||||
}
|
||||
|
||||
void Internal::setMgrFn(lua_State* L, CConfigManager* mgr, const char* name, lua_CFunction fn) {
|
||||
lua_pushlightuserdata(L, mgr);
|
||||
lua_pushcclosure(L, fn, 1);
|
||||
|
|
|
|||
|
|
@ -181,6 +181,9 @@ namespace Config::Lua::Bindings::Internal {
|
|||
|
||||
void setFn(lua_State* L, const char* name, lua_CFunction fn);
|
||||
void setMgrFn(lua_State* L, CConfigManager* mgr, const char* name, lua_CFunction fn);
|
||||
void markDispatcherTable(lua_State* L);
|
||||
int wrapDispatcher(lua_State* L);
|
||||
bool pushDispatcherFunction(lua_State* L, int idx);
|
||||
|
||||
template <typename T>
|
||||
SParseError parseTableField(lua_State* L, int tableIdx, const char* field, T& parser) {
|
||||
|
|
|
|||
|
|
@ -132,13 +132,12 @@ static int hlBind(lua_State* L) {
|
|||
if (auto res = parseKeyString(kb, keys); !res)
|
||||
return Internal::configError(L, std::format("hl.bind: failed to parse key string: {}", res.error()));
|
||||
|
||||
if (!lua_isfunction(L, 2))
|
||||
if (!Internal::pushDispatcherFunction(L, 2))
|
||||
return Internal::configError(L, "hl.bind: dispatcher must be a dispatcher (e.g. hl.dsp.window.close()) or a lua function");
|
||||
|
||||
if (kb.catchAll && mgr->m_currentSubmap.empty())
|
||||
return Internal::configError(L, "hl.bind: catchall keybinds are only allowed in submaps.");
|
||||
|
||||
lua_pushvalue(L, 2);
|
||||
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
kb.handler = "__lua";
|
||||
kb.arg = std::to_string(ref);
|
||||
|
|
@ -293,10 +292,9 @@ static int hlExecCmd(lua_State* L) {
|
|||
}
|
||||
|
||||
static int hlDispatch(lua_State* L) {
|
||||
if (!lua_isfunction(L, 1))
|
||||
return Internal::configError(L, "hl.dispatch: expected a dispatcher function (e.g. hl.dsp.window.close())");
|
||||
if (!Internal::pushDispatcherFunction(L, 1))
|
||||
return Internal::configError(L, "hl.dispatch: expected a dispatcher (e.g. hl.dsp.window.close())");
|
||||
|
||||
lua_pushvalue(L, 1);
|
||||
int status = LUA_OK;
|
||||
if (auto* mgr = CConfigManager::fromLuaState(L); mgr)
|
||||
status = mgr->guardedPCall(0, 1, 0, CConfigManager::LUA_TIMEOUT_DISPATCH_MS, "hl.dispatch");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue