lua: add mechanism for script provided key bindings

There was already an undocumented mechanism provided by
mp.set_key_bindings and other functions, but this was relatively
verbose, and also weird. It was mainly to make the OSC happy (including
being efficient and supporting weird corner cases), while the new
functions try to be a bit simpler.

This also provides a way to let users rebind script-provided commands.

(This mechanism is less efficient, because it's O(n^2) for n added key
bindings, but it shouldn't matter.)
This commit is contained in:
wm4 2014-02-17 02:38:07 +01:00
parent 12b7850f11
commit 24fa69dbfa
3 changed files with 188 additions and 3 deletions

View File

@ -92,6 +92,97 @@ The ``mp`` module is preloaded, although it can be loaded manually with
Return the current mpv internal time in seconds as a number. This is
basically the system time, with an arbitrary offset.
``mp.register_script_command(name, fn)``
Register a command named ``name``. If the script receives a message
with the given name as first argument, ``fn(...)`` is called with the
rest of the script commands.
If a command with the given name already exists, it's overwritten.
This is intended for allowing users to interact the script in some ways
using the ``script_message`` input command.
Example:
In a script, say ``fooscript.lua``:
::
function something_handler(arg1, arg2)
print("arg1=" .. arg1)
print("arg2=" .. arg2)
end
mp.register_script_command("something", something_handler)
input.conf:
::
x script_message lua/fooscript something "hello" "you"
This will print the lines ``arg1=hello`` and ``arg2=something`` when the
key ``x`` is pressed.
Also see ``mp.add_key_binding`` how to add key bindings by default.
``mp.unregister_script_command(name)``
Undo a previous registration with ``mp.register_script_command``. Does
nothing if the ``name`` wasn't registered.
``mp.add_key_binding(key, name|fn [,fn])``
Register a key binding. The binding will be mapped to the given ``key``,
which is a string describing the physical key. This uses the same key
names as in input.conf, and also allows combinations (e.g. ``ctrl+a``).
Key bindings are dispatched as script commands. The ``name`` argument is
the name used to invoke command handlers as registered by
``mp.register_script_command``. The name can also be used by users to remap
the bindings provided by your script (see below).
If a key binding or a command with the given name already exists, it's
overwritten.
The ``fn`` parameter is optional. If provided, it must be a function, and
will be called when the key is pressed. Actually, this just for
convenience, and literally calls ``mp.register_script_command(name, fn)``.
You can also omit the name and only provide a function ``fn`` instead. Then
a random name is generated internally.
Example:
::
function something_handler()
print("the key was pressed")
end
mp.add_key_binding("x", "something", something_handler)
This will print the message ``the key was pressed`` when ``x`` was pressed.
The user can remap these key bindings. Assume the above script was using
the filename ``fooscript.lua``, then the user has to put the following
into his input.conf to remap the command to the ``y`` key:
::
y script_message lua/fooscript something
This will print the message when the key ``y`` is pressed. (``x`` will
still work, unless the user overmaps it.)
``mp.add_forced_key_binding(...)``
This works almost the same as ``mp.add_key_binding``, but registers the
key binding in a way that will overwrite the user's custom bindings in his
input.conf. (``mp.add_key_binding`` overwrites default key bindings only,
but not those by the user's input.conf.)
``mp.remove_key_binding(name)``
Remove a key binding added with ``mp.add_key_binding`` or
``mp.add_forced_key_binding``. Use the same name as you used when adding
the bindings. It's not possible to remove bindings for which you omitted
the name.
``mp.register_event(name, fn)``
Call a specific function when an event happens. The event name is a string,
and the function fn is a Lua function value.
@ -136,7 +227,14 @@ The ``mp`` module is preloaded, although it can be loaded manually with
this equally, so you should be careful about collisions.
``mp.get_script_name()``
Return the name of the current script.
Return the name of the current script. The name is usually made of the
filename of the script, with directory and file extension removed, and
prefixed with ``lua/``. If there are several script which would have the
same name, it's made unique by appending a number.
.. admonition:: Example
The script ``/path/to/fooscript.lua`` becomes ``lua/fooscript``.
``mp.suspend()``
Suspend the mpv main loop. There is a long-winded explanation of this in

View File

@ -666,7 +666,18 @@ static int script_input_define_section(lua_State *L)
struct MPContext *mpctx = get_mpctx(L);
char *section = (char *)luaL_checkstring(L, 1);
char *contents = (char *)luaL_checkstring(L, 2);
mp_input_define_section(mpctx->input, section, "<script>", contents, true);
char *flags = (char *)luaL_optstring(L, 3, "");
bool builtin = true;
if (strcmp(flags, "builtin") == 0) {
builtin = true;
} else if (strcmp(flags, "default") == 0) {
builtin = false;
} else if (strcmp(flags, "") == 0) {
//pass
} else {
luaL_error(L, "invalid flags: '%*'", flags);
}
mp_input_define_section(mpctx->input, section, "<script>", contents, builtin);
return 0;
}

View File

@ -4,7 +4,7 @@ end
local callbacks = {}
-- each script has its own section, so that they don't conflict
local default_section = "input_" .. mp.script_name
local default_section = "input_dispatch_" .. mp.script_name
-- Set the list of key bindings. These will override the user's bindings, so
-- you should use this sparingly.
@ -66,6 +66,64 @@ local function script_dispatch(event)
end
end
-- "Newer" and more convenient API
local key_bindings = {}
local command_id = 1
local function update_key_bindings()
for i = 1, 2 do
local section, flags
local def = i == 1
if def then
section = "input_" .. mp.script_name
flags = "builtin"
else
section = "input_forced_" .. mp.script_name
flags = "default"
end
local cfg = ""
for k, v in pairs(key_bindings) do
if v.forced ~= def then
cfg = cfg .. v.key .. " script_message " .. mp.script_name
.. " " .. v.name .. "\n"
end
end
mp.input_define_section(section, cfg, flags)
-- TODO: remove the section if the script is stopped
mp.input_enable_section(section)
end
end
local function add_binding(attrs, key, name, fn)
if (type(name) ~= "string") and (not fn) then
fn = name
name = "command" .. tostring(command_id)
command_id = command_id + 1
end
attrs.key = key
attrs.name = name
key_bindings[name] = attrs
update_key_bindings()
if fn then
mp.register_script_command(name, fn)
end
end
function mp.add_key_binding(...)
add_binding({forced=false}, ...)
end
function mp.add_forced_key_binding(...)
add_binding({forced=true}, ...)
end
function mp.remove_key_binding(name)
key_bindings[name] = nil
update_key_bindings()
mp.unregister_script_command(name)
end
local timers = {}
function mp.add_timeout(seconds, cb)
@ -125,6 +183,23 @@ local function process_timers()
end
end
local commands = {}
function mp.register_script_command(name, fn)
commands[name] = fn
end
function mp.unregister_script_command(name)
commands[name] = nil
end
local function command_dispatch(ev)
if #ev.args > 0 then
local handler = commands[ev.args[1]]
handler(unpack(ev.args, 2))
end
end
-- used by default event loop (mp_event_loop()) to decide when to quit
mp.keep_running = true
@ -143,6 +218,7 @@ end
-- default handlers
mp.register_event("shutdown", function() mp.keep_running = false end)
mp.register_event("script-input-dispatch", script_dispatch)
mp.register_event("client-message", command_dispatch)
mp.msg = {
log = mp.log,