3
0
mirror of https://github.com/hyprwm/Hyprland.git synced 2026-08-18 11:02:10 +00:00

config/lua: display dispatcher names in binds list and when stringifying (#15722)

This commit is contained in:
Lucas Ritzdorf
2026-08-08 14:41:40 -06:00
committed by GitHub
parent a2636192b5
commit 006bcc69a1
4 changed files with 52 additions and 14 deletions

View File

@ -831,3 +831,17 @@ TEST_CASE(keybinds) {
CALL_SUBTEST(perDeviceKeybind);
CALL_SUBTEST(unbind);
}
TEST_CASE(luaDispatcherStrings) {
OK(getFromSocket("/eval B = hl.bind('SUPER + F24', hl.dsp.exec_cmd('true'))"));
EXPECT(getFromSocket("/repl return B.handler"), "HL.Dispatcher(exec_cmd)");
OK(getFromSocket("/eval B:unbind()"));
OK(getFromSocket("/eval B = hl.bind('SUPER + F24', hl.dsp.window.close())"));
EXPECT(getFromSocket("/repl return B.handler"), "HL.Dispatcher(close)");
OK(getFromSocket("/eval B:unbind()"));
OK(getFromSocket("/eval B = hl.bind('SUPER + F24', function() hl.exec_cmd('true') end)"));
EXPECT_STARTS_WITH(getFromSocket("/repl return B.handler"), "function: ");
OK(getFromSocket("/eval B:unbind()"));
}

View File

@ -7,7 +7,8 @@ static char DISPATCHER_TABLES_REGISTRY_KEY;
namespace {
struct SDispatcherRef {
int ref = LUA_NOREF;
int ref = LUA_NOREF;
int nameref = LUA_NOREF;
};
}
@ -17,6 +18,10 @@ static int dispatcherGc(lua_State* L) {
luaL_unref(L, LUA_REGISTRYINDEX, dispatcher->ref);
dispatcher->ref = LUA_NOREF;
}
if (dispatcher->nameref != LUA_NOREF) {
luaL_unref(L, LUA_REGISTRYINDEX, dispatcher->nameref);
dispatcher->nameref = LUA_NOREF;
}
return 0;
}
@ -26,7 +31,15 @@ static int dispatcherCall(lua_State* L) {
}
static int dispatcherToString(lua_State* L) {
lua_pushstring(L, "HL.Dispatcher");
auto* dispatcher = sc<SDispatcherRef*>(luaL_checkudata(L, 1, DISPATCHER_MT));
std::string str;
if (dispatcher->nameref != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, dispatcher->nameref);
str = lua_tostring(L, -1);
lua_pop(L, 1);
} else
str = "INVALID";
lua_pushstring(L, std::format("HL.Dispatcher({})", str).c_str());
return 1;
}
@ -74,8 +87,10 @@ static int dispatcherFactory(lua_State* L) {
lua_call(L, nargs, LUA_MULTRET);
const int nresults = lua_gettop(L);
if (nresults == 1 && lua_isfunction(L, -1))
if (nresults == 1 && lua_isfunction(L, -1)) {
lua_pushvalue(L, lua_upvalueindex(2));
return Internal::wrapDispatcher(L);
}
return nresults;
}
@ -83,7 +98,8 @@ static int dispatcherFactory(lua_State* L) {
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);
lua_pushstring(L, name);
lua_pushcclosure(L, dispatcherFactory, 2);
} else
lua_pushcfunction(L, fn);
@ -112,11 +128,13 @@ void Internal::markDispatcherTable(lua_State* L) {
}
int Internal::wrapDispatcher(lua_State* L) {
luaL_checktype(L, -1, LUA_TFUNCTION);
luaL_checktype(L, -1, LUA_TSTRING);
const int nameref = luaL_ref(L, LUA_REGISTRYINDEX);
luaL_checktype(L, -1, LUA_TFUNCTION);
const int ref = luaL_ref(L, LUA_REGISTRYINDEX);
new (lua_newuserdata(L, sizeof(SDispatcherRef))) SDispatcherRef{.ref = ref};
new (lua_newuserdata(L, sizeof(SDispatcherRef))) SDispatcherRef{.ref = ref, .nameref = nameref};
ensureDispatcherMetatable(L);
luaL_getmetatable(L, DISPATCHER_MT);

View File

@ -25,6 +25,7 @@ using namespace Hyprutils::String;
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <xkbcommon/xkbcommon.h>
}
@ -79,6 +80,9 @@ static int hlBind(lua_State* L) {
if (!keys)
return Internal::configError(L, std::format("hl.bind: failed to parse key string: {}", keys.error()));
const std::string handler = luaL_tolstring(L, 2, nullptr);
lua_pop(L, 1);
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");
@ -94,7 +98,7 @@ static int hlBind(lua_State* L) {
.metadata =
{
.displayKey = std::string{DISPLAY_KEYS},
.handler = "__lua",
.handler = handler,
.argument = std::to_string(LUA_REF->ref()),
.submap = mgr->m_currentSubmap,
.submapReset = mgr->m_currentSubmapReset,

View File

@ -146,8 +146,8 @@ TEST(ConfigLuaObjects, keybindExposesMetadataAndRemoveMethods) {
{
.displayKey = "SUPER + Q",
.description = "Close active window",
.handler = "exec",
.argument = "kitty",
.handler = "HL.Dispatcher(close)",
.argument = "42",
.submap = "default",
},
});
@ -159,13 +159,13 @@ TEST(ConfigLuaObjects, keybindExposesMetadataAndRemoveMethods) {
Objects::CLuaKeybind::push(L, keybind);
lua_setglobal(L, "kb");
ASSERT_EQ(luaL_dostring(L, R"(
ASSERT_EQ(luaL_dostring(L, R"-(
assert(kb.enabled == true)
assert(kb.description == "Close active window")
assert(kb.display_key == "SUPER + Q")
assert(kb.submap == "default")
assert(kb.handler == "exec")
assert(kb.arg == "kitty")
assert(kb.handler == "HL.Dispatcher(close)")
assert(kb.arg == "42")
assert(kb.modmask ~= nil)
assert(kb.key == "Q")
assert(kb.keycode == 0)
@ -178,7 +178,7 @@ TEST(ConfigLuaObjects, keybindExposesMetadataAndRemoveMethods) {
kb:remove()
kb:unbind()
)"),
)-"),
LUA_OK);
ASSERT_EQ(Keybinds::mgr()->registry().size(), 1);
@ -211,13 +211,15 @@ TEST(ConfigLuaObjects, keybindRemovalDoesNotUnrefCallback) {
ASSERT_EQ(luaL_dostring(L, "return function() end"), LUA_OK);
ASSERT_TRUE(lua_isfunction(L, -1));
const std::string HANDLER = luaL_tolstring(L, -1, nullptr);
lua_pop(L, 1);
const int REF = luaL_ref(L, LUA_REGISTRYINDEX);
const auto keybind = Keybinds::mgr()->addBind(makeBind({"Q"}, 0,
{
.metadata =
{
.handler = "__lua",
.handler = HANDLER,
.argument = std::to_string(REF),
},
}));