aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2016-02-06 21:47:35 +0100
committerMarc André Tanner <mat@brain-dump.org>2016-02-18 16:50:39 +0100
commitd55bdfe229e8e1fcda4a15dd977f0abce7a40ccc (patch)
tree5b8dba1403719a0b9c71c191df92f56214d2b9cc
parent0869289348bdd8206d0128b720507ffb857459a5 (diff)
downloadvis-d55bdfe229e8e1fcda4a15dd977f0abce7a40ccc.tar.gz
vis-d55bdfe229e8e1fcda4a15dd977f0abce7a40ccc.tar.xz
vis: add possibility to bind keys to lua functions
-rw-r--r--vis-lua.c85
-rw-r--r--vis.h3
2 files changed, 87 insertions, 1 deletions
diff --git a/vis-lua.c b/vis-lua.c
index 7549cc1..30ac0a3 100644
--- a/vis-lua.c
+++ b/vis-lua.c
@@ -100,6 +100,37 @@ static int pcall(Vis *vis, lua_State *L, int nargs, int nresults) {
return ret;
}
+/* expects a lua function at the top of the stack and stores a
+ * reference to it in the registry. The return value can be used
+ * to look it up.
+ *
+ * registry["vis.functions"][(void*)(function)] = function
+ */
+static const void *func_ref_new(lua_State *L) {
+ const void *addr = lua_topointer(L, -1);
+ if (!lua_isfunction(L, -1) || !addr)
+ return NULL;
+ lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
+ lua_pushlightuserdata(L, (void*)addr);
+ lua_pushvalue(L, -3);
+ lua_settable(L, -3);
+ lua_pop(L, 1);
+ return addr;
+}
+
+/* retrieve function from registry and place it at the top of the stack */
+static bool func_ref_get(lua_State *L, const void *addr) {
+ lua_getfield(L, LUA_REGISTRYINDEX, "vis.functions");
+ lua_pushlightuserdata(L, (void*)addr);
+ lua_gettable(L, -2);
+ lua_remove(L, -2);
+ if (!lua_isfunction(L, -1)) {
+ lua_pop(L, 1);
+ return false;
+ }
+ return true;
+}
+
static void *obj_new(lua_State *L, size_t size, const char *type) {
void *obj = lua_newuserdata(L, size);
luaL_getmetatable(L, type);
@@ -193,6 +224,14 @@ static int newindex_common(lua_State *L) {
return 0;
}
+static const char *keymapping(Vis *vis, const char *keys, const Arg *arg) {
+ lua_State *L = vis->lua;
+ if (!func_ref_get(L, arg->v))
+ return keys;
+ pcall(vis, L, 0, 0);
+ return keys;
+}
+
static int windows_iter(lua_State *L);
static int windows(lua_State *L) {
@@ -261,12 +300,55 @@ static int open(lua_State *L) {
return 1;
}
+static int map(lua_State *L) {
+ KeyBinding *binding = NULL;
+ KeyAction *action = NULL;
+ Vis *vis = lua_touserdata(L, lua_upvalueindex(1));
+
+ int mode = luaL_checkint(L, 1);
+ const char *key = luaL_checkstring(L, 2);
+
+ if (!key || !lua_isfunction(L, 3))
+ goto err;
+ if (!(binding = calloc(1, sizeof *binding)) || !(action = calloc(1, sizeof *action)))
+ goto err;
+
+ /* store reference to function in the registry */
+ lua_pushvalue(L, 3);
+ const void *func = func_ref_new(L);
+ if (!func)
+ goto err;
+
+ *action = (KeyAction){
+ .name = NULL,
+ .help = NULL,
+ .func = keymapping,
+ .arg = (const Arg){
+ .v = func,
+ },
+ };
+
+ binding->action = action;
+
+ if (!vis_mode_map(vis, mode, key, binding))
+ goto err;
+
+ lua_pushboolean(L, true);
+ return 1;
+err:
+ free(binding);
+ free(action);
+ lua_pushboolean(L, false);
+ return 1;
+}
+
static const struct luaL_Reg vis_lua[] = {
{ "files", files },
{ "windows", windows },
{ "command", command },
{ "info", info },
{ "open", open },
+ { "map", map },
{ NULL, NULL },
};
@@ -591,6 +673,9 @@ void vis_lua_start(Vis *vis) {
/* table in registry to track lifetimes of C objects */
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, "vis.objects");
+ /* table in registry to store references to Lua functions */
+ lua_newtable(L);
+ lua_setfield(L, LUA_REGISTRYINDEX, "vis.functions");
/* metatable used to type check user data */
luaL_newmetatable(L, "vis.file");
luaL_setfuncs(L, file_funcs, 0);
diff --git a/vis.h b/vis.h
index dc087f9..560e4d9 100644
--- a/vis.h
+++ b/vis.h
@@ -26,6 +26,7 @@ typedef union { /* various types of arguments passed to key action functions */
bool b;
int i;
const char *s;
+ const void *v;
void (*w)(View*);
void (*f)(Vis*);
} Arg;
@@ -39,7 +40,7 @@ typedef struct { /* a KeyAction can be bound to a key binding */
* to complete the action. In this case the function will be called again when more input
* becomes available */
const char* (*func)(Vis*, const char *keys, const Arg*);
- const Arg arg; /* additional arguments which will be passed as to func */
+ Arg arg; /* additional arguments which will be passed as to func */
} KeyAction;
typedef struct { /* a key binding either refers to an action or an alias */