summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/client-api-changes.rst2
-rw-r--r--DOCS/man/input.rst22
-rw-r--r--DOCS/man/lua.rst25
-rw-r--r--input/cmd_list.c2
-rw-r--r--input/cmd_list.h2
-rw-r--r--input/input.c14
-rw-r--r--input/input.h9
-rw-r--r--libmpv/client.h26
-rw-r--r--player/client.c24
-rw-r--r--player/client.h2
-rw-r--r--player/command.c30
-rw-r--r--player/lua/defaults.lua132
12 files changed, 211 insertions, 79 deletions
diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst
index 9490fa4b7a..177c70cb16 100644
--- a/DOCS/client-api-changes.rst
+++ b/DOCS/client-api-changes.rst
@@ -25,6 +25,8 @@ API changes
::
+ 1.10 - deprecate/disable everything directly related to script_dispatch
+ (most likely affects nobody)
1.9 - add enum mpv_end_file_reason for mpv_event_end_file.reason
- add MPV_END_FILE_REASON_ERROR and the mpv_event_end_file.error field
for slightly better error reporting on playback failure
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst
index 3e4fb300f8..a1cca52191 100644
--- a/DOCS/man/input.rst
+++ b/DOCS/man/input.rst
@@ -541,8 +541,26 @@ Input Commands that are Possibly Subject to Change
``<target>``. Each client (scripts etc.) has a unique name. For example,
Lua scripts can get their name via ``mp.get_script_name()``.
- (Scripts use this internally to dispatch key bindings, and this can also
- be used in input.conf to reassign such bindings.)
+``script_binding "<name>"``
+ Invoke a script-provided key binding. This can be used to remap key
+ bindings provided by external Lua scripts.
+
+ The argument is the name of the binding.
+
+ It can optionally be prefixed with the name of the script, using ``/`` as
+ separator, e.g. ``script_binding scriptname/bindingname``.
+
+ For completeness, here is how this command works internally. The details
+ could change any time. On any matching key event, ``script_message_to``
+ or ``script_message`` is called (depending on whether the script name is
+ included), where the first argument is the string ``key-binding``, the
+ second argument is the name of the binding, and the third argument is the
+ key state as string. The key state consists of a number of letters. The
+ first letter is one of ``d`` (key was pressed down), ``u`` (was released),
+ ``r`` (key is still down, and was repeated; only if key repeat is enabled
+ for this binding), ``p`` (key was pressed; happens if up/down can't be
+ tracked). The second letter whether the event originates from the mouse,
+ either ``m`` (mouse button) or ``-`` (something else).
``ab_loop``
Cycle through A-B loop states. The first command will set the ``A`` point
diff --git a/DOCS/man/lua.rst b/DOCS/man/lua.rst
index e09792a277..3f9e90e0b0 100644
--- a/DOCS/man/lua.rst
+++ b/DOCS/man/lua.rst
@@ -198,12 +198,23 @@ The ``mp`` module is preloaded, although it can be loaded manually with
overwritten. You can omit the name, in which case a random name is generated
internally.
- The last argument is used for additional flags. Currently, this includes
- the string ``repeatable``, which enables key repeat for this specific
- binding.
+ The last argument is used for optional flags. This is a table, which can
+ have the following entries:
- Internally, key bindings are dispatched via the ``script_message_to`` input
- command and ``mp.register_script_message``.
+ ``repeatable``
+ If set to ``true``, enables key repeat for this specific binding.
+
+ ``complex``
+ If set to ``true``, then ``fn`` is called on both key up and down
+ events (as well as key repeat, if enabled), with the first
+ argument being a table. This table has an ``event`` entry, which
+ is set to one of the strings ``down``, ``repeat``, ``up`` or
+ ``press`` (the latter if key up/down can't be tracked). It further
+ has an ``is_mouse`` entry, which tells whether the event was caused
+ by a mouse button.
+
+ Internally, key bindings are dispatched via the ``script_message_to`` or
+ ``script_binding`` input commands and ``mp.register_script_message``.
Trying to map multiple commands to a key will essentially prefer a random
binding, while the other bindings are not called. It is guaranteed that
@@ -226,7 +237,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
::
- y script_message something
+ y script_binding something
This will print the message when the key ``y`` is pressed. (``x`` will
@@ -237,7 +248,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
::
- y script_message_to fooscript something
+ y script_binding fooscript.something
``mp.add_forced_key_binding(...)``
This works almost the same as ``mp.add_key_binding``, but registers the
diff --git a/input/cmd_list.c b/input/cmd_list.c
index e0e307b4c4..1edc28d521 100644
--- a/input/cmd_list.c
+++ b/input/cmd_list.c
@@ -166,7 +166,7 @@ const struct mp_cmd_def mp_cmds[] = {
{ MP_CMD_VO_CMDLINE, "vo_cmdline", { ARG_STRING } },
- { MP_CMD_SCRIPT_DISPATCH, "script_dispatch", { ARG_STRING, ARG_INT } },
+ { MP_CMD_SCRIPT_BINDING, "script_binding", { ARG_STRING } },
{ MP_CMD_SCRIPT_MESSAGE, "script_message", { ARG_STRING }, .vararg = true },
{ MP_CMD_SCRIPT_MESSAGE_TO, "script_message_to", { ARG_STRING, ARG_STRING },
.vararg = true },
diff --git a/input/cmd_list.h b/input/cmd_list.h
index 638fc5bb68..6705d3f803 100644
--- a/input/cmd_list.h
+++ b/input/cmd_list.h
@@ -93,7 +93,7 @@ enum mp_command_type {
MP_CMD_VO_CMDLINE,
/// Internal for Lua scripts
- MP_CMD_SCRIPT_DISPATCH,
+ MP_CMD_SCRIPT_BINDING,
MP_CMD_SCRIPT_MESSAGE,
MP_CMD_SCRIPT_MESSAGE_TO,
diff --git a/input/input.c b/input/input.c
index 4133396399..7c647adef3 100644
--- a/input/input.c
+++ b/input/input.c
@@ -476,6 +476,7 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, char *force_section,
keyname, cmd->cmd, ret->input_section);
talloc_free(keyname);
}
+ ret->is_mouse_button = code & MP_KEY_EMIT_ON_UP;
} else {
char *key_buf = mp_input_get_key_combo_name(&code, 1);
MP_ERR(ictx, "Invalid command for bound key '%s': '%s'\n",
@@ -518,6 +519,7 @@ static void release_down_cmd(struct input_ctx *ictx, bool drop_current)
{
memset(ictx->key_history, 0, sizeof(ictx->key_history));
ictx->current_down_cmd->key_up_follows = false;
+ ictx->current_down_cmd->is_up = true;
mp_input_queue_cmd(ictx, ictx->current_down_cmd);
} else {
talloc_free(ictx->current_down_cmd);
@@ -534,7 +536,7 @@ static void release_down_cmd(struct input_ctx *ictx, bool drop_current)
static bool key_updown_ok(enum mp_command_type cmd)
{
switch (cmd) {
- case MP_CMD_SCRIPT_DISPATCH:
+ case MP_CMD_SCRIPT_BINDING:
return true;
default:
return false;
@@ -589,8 +591,11 @@ static void interpret_key(struct input_ctx *ictx, int code, double scale)
// Cancel current down-event (there can be only one)
release_down_cmd(ictx, true);
cmd = resolve_key(ictx, code);
- if (cmd && (code & MP_KEY_EMIT_ON_UP))
- cmd->key_up_follows = true;
+ if (cmd) {
+ cmd->is_up_down = true;
+ cmd->key_up_follows = (code & MP_KEY_EMIT_ON_UP) |
+ key_updown_ok(cmd->id);
+ }
ictx->last_key_down = code;
ictx->last_key_down_time = mp_time_us();
ictx->ar_state = 0;
@@ -885,8 +890,7 @@ mp_cmd_t *mp_input_read_cmd(struct input_ctx *ictx)
struct mp_cmd *ret = queue_remove_head(&ictx->cmd_queue);
if (!ret) {
ret = check_autorepeat(ictx);
- // (if explicitly repeated, don't let command.c ignore it)
- if (ret && !(ret->flags & MP_ALLOW_REPEAT))
+ if (ret)
ret->repeated = true;
}
if (ret && ret->mouse_move) {
diff --git a/input/input.h b/input/input.h
index 6da1f719c2..02e9d2f86b 100644
--- a/input/input.h
+++ b/input/input.h
@@ -76,9 +76,12 @@ typedef struct mp_cmd {
int flags; // mp_cmd_flags bitfield
bstr original;
char *input_section;
- bool key_up_follows;
- bool repeated;
- bool mouse_move;
+ bool is_up_down : 1;
+ bool is_up : 1;
+ bool is_mouse_button : 1;
+ bool key_up_follows : 1;
+ bool repeated : 1;
+ bool mouse_move : 1;
int mouse_x, mouse_y;
struct mp_cmd *queue_next;
double scale; // for scaling numeric arguments
diff --git a/libmpv/client.h b/libmpv/client.h
index 4ad0a5e65a..11b27b26df 100644
--- a/libmpv/client.h
+++ b/libmpv/client.h
@@ -164,7 +164,7 @@ extern "C" {
* relational operators (<, >, <=, >=).
*/
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
-#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 9)
+#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 10)
/**
* Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with.
@@ -1010,12 +1010,12 @@ typedef enum mpv_event_id {
*/
MPV_EVENT_TICK = 14,
/**
- * Triggered by the script_dispatch input command. The command uses the
- * client name (see mpv_client_name()) to dispatch keyboard or mouse input
- * to a client.
- * (This is pretty obscure and largely replaced by MPV_EVENT_CLIENT_MESSAGE,
- * but still the only way to distinguish key down/up events when binding
- * script_dispatch via input.conf.)
+ * @deprecated This was used internally with the internal "script_dispatch"
+ * command to dispatch keyboard and mouse input for the OSC.
+ * It was never useful in general and has been completely
+ * replaced with "script_binding".
+ * This event never happens anymore, and is included in this
+ * header only for compatibility.
*/
MPV_EVENT_SCRIPT_INPUT_DISPATCH = 15,
/**
@@ -1211,17 +1211,10 @@ typedef struct mpv_event_end_file {
int error;
} mpv_event_end_file;
+/** @deprecated see MPV_EVENT_SCRIPT_INPUT_DISPATCH for remarks
+ */
typedef struct mpv_event_script_input_dispatch {
- /**
- * Arbitrary integer value that was provided as argument to the
- * script_dispatch input command.
- */
int arg0;
- /**
- * Type of the input. Currently either "keyup_follows" (basically a key
- * down event), or "press" (either a single key event, or a key up event
- * following a "keyup_follows" event).
- */
const char *type;
} mpv_event_script_input_dispatch;
@@ -1269,7 +1262,6 @@ typedef struct mpv_event {
* MPV_EVENT_GET_PROPERTY_REPLY: mpv_event_property*
* MPV_EVENT_PROPERTY_CHANGE: mpv_event_property*
* MPV_EVENT_LOG_MESSAGE: mpv_event_log_message*
- * MPV_EVENT_SCRIPT_INPUT_DISPATCH: mpv_event_script_input_dispatch*
* MPV_EVENT_CLIENT_MESSAGE: mpv_event_client_message*
* MPV_EVENT_END_FILE: mpv_event_end_file*
* other: NULL
diff --git a/player/client.c b/player/client.c
index 97bb215464..d01c611a3d 100644
--- a/player/client.c
+++ b/player/client.c
@@ -589,9 +589,16 @@ void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data)
pthread_mutex_unlock(&clients->lock);
}
+// If client_name == NULL, then broadcast and free the event.
int mp_client_send_event(struct MPContext *mpctx, const char *client_name,
int event, void *data)
{
+ if (!client_name) {
+ mp_client_broadcast_event(mpctx, event, data);
+ talloc_free(data);
+ return 0;
+ }
+
struct mp_client_api *clients = mpctx->clients;
int r = 0;
@@ -615,6 +622,23 @@ int mp_client_send_event(struct MPContext *mpctx, const char *client_name,
return r;
}
+int mp_client_send_event_dup(struct MPContext *mpctx, const char *client_name,
+ int event, void *data)
+{
+ if (!client_name) {
+ mp_client_broadcast_event(mpctx, event, data);
+ return 0;
+ }
+
+ struct mpv_event event_data = {
+ .event_id = event,
+ .data = data,
+ };
+
+ dup_event_data(&event_data);
+ return mp_client_send_event(mpctx, client_name, event, event_data.data);
+}
+
int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable)
{
if (!mpv_event_name(event) || enable < 0 || enable > 1)
diff --git a/player/client.h b/player/client.h
index bdecae20ae..a275bb9728 100644
--- a/player/client.h
+++ b/player/client.h
@@ -23,6 +23,8 @@ bool mp_client_exists(struct MPContext *mpctx, const char *client_name);
void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data);
int mp_client_send_event(struct MPContext *mpctx, const char *client_name,
int event, void *data);
+int mp_client_send_event_dup(struct MPContext *mpctx, const char *client_name,
+ int event, void *data);
bool mp_client_event_is_registered(struct MPContext *mpctx, int event);
void mp_client_property_change(struct MPContext *mpctx, const char *name);
diff --git a/player/command.c b/player/command.c
index 30768e9b71..0042d8dee7 100644
--- a/player/command.c
+++ b/player/command.c
@@ -4471,17 +4471,29 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
return edit_filters_osd(mpctx, STREAM_VIDEO, cmd->args[0].v.s,
cmd->args[1].v.s, msg_osd);
- case MP_CMD_SCRIPT_DISPATCH: {
- mpv_event_script_input_dispatch *event = talloc_ptrtype(NULL, event);
- *event = (mpv_event_script_input_dispatch){
- .arg0 = cmd->args[1].v.i,
- .type = cmd->key_up_follows ? "keyup_follows" : "press",
- };
- if (mp_client_send_event(mpctx, cmd->args[0].v.s,
- MPV_EVENT_SCRIPT_INPUT_DISPATCH, event) < 0)
+ case MP_CMD_SCRIPT_BINDING: {
+ mpv_event_client_message event = {0};
+ char *name = cmd->args[0].v.s;
+ if (!name || !name[0])
+ return -1;
+ char *sep = strchr(name, '/');
+ char *target = NULL;
+ char space[MAX_CLIENT_NAME];
+ if (name) {
+ snprintf(space, sizeof(space), "%.*s", (int)(sep - name), name);
+ target = space;
+ name = sep + 1;
+ }
+ char state[3] = {'p', cmd->is_mouse_button ? 'm' : '-'};
+ if (cmd->is_up_down)
+ state[0] = cmd->repeated ? 'r' : (cmd->is_up ? 'u' : 'd');
+ event.num_args = 3;
+ event.args = (const char*[3]){"key-binding", name, state};
+ if (mp_client_send_event_dup(mpctx, target,
+ MPV_EVENT_CLIENT_MESSAGE, &event) < 0)
{
MP_VERBOSE(mpctx, "Can't find script '%s' when handling input.\n",
- cmd->args[0].v.s);
+ target ? target : "-");
return -1;
}
break;
diff --git a/player/lua/defaults.lua b/player/lua/defaults.lua
index 4b3be12ac9..812bb71afe 100644
--- a/player/lua/defaults.lua
+++ b/player/lua/defaults.lua
@@ -22,7 +22,27 @@ function mp.get_opt(key, def)
return val
end
-local callbacks = {}
+-- For dispatching script_binding. This is sent as:
+-- script_message_to $script_name $binding_name $keystate
+-- The array is indexed by $binding_name, and has functions like this as value:
+-- fn($binding_name, $keystate)
+local dispatch_key_bindings = {}
+
+local message_id = 0
+local function reserve_binding()
+ message_id = message_id + 1
+ return "__keybinding" .. tostring(message_id)
+end
+
+local function dispatch_key_binding(name, state)
+ local fn = dispatch_key_bindings[name]
+ if fn then
+ fn(name, state)
+ end
+end
+
+-- "Old", deprecated API
+
-- each script has its own section, so that they don't conflict
local default_section = "input_dispatch_" .. mp.script_name
@@ -34,13 +54,8 @@ local default_section = "input_dispatch_" .. mp.script_name
-- Note: the bindings are not active by default. Use enable_key_bindings().
--
-- list is an array of key bindings, where each entry is an array as follow:
--- {key, callback}
--- {key, callback, callback_down}
+-- {key, callback_press, callback_down, callback_up}
-- key is the key string as used in input.conf, like "ctrl+a"
--- callback is a Lua function that is called when the key binding is used.
--- callback_down can be given too, and is called when a mouse button is pressed
--- if the key is a mouse button. (The normal callback will be for mouse button
--- down.)
--
-- callback can be a string too, in which case the following will be added like
-- an input.conf line: key .. " " .. callback
@@ -52,10 +67,29 @@ function mp.set_key_bindings(list, section, flags)
local key = entry[1]
local cb = entry[2]
local cb_down = entry[3]
- if type(cb) == "function" then
- callbacks[#callbacks + 1] = {press=cb, before_press=cb_down}
- cfg = cfg .. key .. " script_dispatch " .. mp.script_name
- .. " " .. #callbacks .. "\n"
+ local cb_up = entry[4]
+ if type(cb) ~= "string" then
+ local mangle = reserve_binding()
+ dispatch_key_bindings[mangle] = function(name, state)
+ local event = state:sub(1, 1)
+ local is_mouse = state:sub(2, 2) == "m"
+ local def = (is_mouse and "u") or "d"
+ if event == "r" then
+ event = "d"
+ end
+ if event == "p" and cb then
+ cb()
+ elseif event == "d" and cb_down then
+ cb_down()
+ elseif event == "u" and cb_up then
+ cb_up()
+ elseif event == def and cb then
+ print("whooo")
+ cb()
+ end
+ end
+ cfg = cfg .. key .. " script_binding " ..
+ mp.script_name .. "/" .. mangle .. "\n"
else
cfg = cfg .. key .. " " .. cb .. "\n"
end
@@ -75,21 +109,9 @@ function mp.set_mouse_area(x0, y0, x1, y1, section)
mp.input_set_section_mouse_area(section or default_section, x0, y0, x1, y1)
end
-local function script_dispatch(event)
- local cb = callbacks[event.arg0]
- if cb then
- if event.type == "press" and cb.press then
- cb.press()
- elseif event.type == "keyup_follows" and cb.before_press then
- cb.before_press()
- end
- end
-end
-
-- "Newer" and more convenient API
local key_bindings = {}
-local message_id = 1
local function update_key_bindings()
for i = 1, 2 do
@@ -105,9 +127,7 @@ local function update_key_bindings()
local cfg = ""
for k, v in pairs(key_bindings) do
if v.forced ~= def then
- local flags = (v.repeatable and " repeatable") or ""
- cfg = cfg .. v.key .. " " .. flags .. " script_message_to "
- .. mp.script_name .. " " .. v.name .. "\n"
+ cfg = cfg .. v.bind .. "\n"
end
end
mp.input_define_section(section, cfg, flags)
@@ -117,19 +137,61 @@ local function update_key_bindings()
end
local function add_binding(attrs, key, name, fn, rp)
+ rp = rp or ""
if (type(name) ~= "string") and (not fn) then
fn = name
- name = "message" .. tostring(message_id)
- message_id = message_id + 1
+ name = reserve_binding()
+ end
+ local bind = key
+ if rp == "repeatable" or rp["repeatable"] then
+ bind = bind .. " repeatable"
+ end
+ if rp["forced"] then
+ attrs.forced = true
+ end
+ local key_cb, msg_cb
+ if not fn then
+ fn = function() end
+ end
+ if rp["complex"] then
+ local key_states = {
+ ["u"] = "up",
+ ["d"] = "down",
+ ["r"] = "repeat",
+ ["p"] = "press",
+ }
+ key_cb = function(name, state)
+ fn({
+ event = key_states[state:sub(1, 1)] or "unknown",
+ is_mouse = state:sub(2, 2) == "m"
+ })
+ end
+ msg_cb = function()
+ fn({event = "press", is_mouse = false})
+ end
+ else
+ key_cb = function(name, state)
+ -- Emulate the same semantics as input.c uses for most bindings:
+ -- For keyboard, "down" runs the command, "up" does nothing;
+ -- for mouse, "down" does nothing, "up" runs the command.
+ -- Also, key repeat triggers the binding again.
+ local event = state:sub(1, 1)
+ local is_mouse = state:sub(2, 2) == "m"
+ if is_mouse and event == "u" then
+ fn()
+ elseif (not is_mouse) and (event == "d" or event == "r") then
+ fn()
+ end
+ end
+ msg_cb = fn
end
- attrs.repeatable = rp == "repeatable"
- attrs.key = key
+ attrs.bind = bind .. " script_binding " .. mp.script_name .. "/" .. name
attrs.name = name
key_bindings[name] = attrs
update_key_bindings()
- if fn then
- mp.register_script_message(name, fn)
- end
+ dispatch_key_bindings[name] = key_cb
+ mp.unregister_script_message(name)
+ mp.register_script_message(name, msg_cb)
end
function mp.add_key_binding(...)
@@ -322,10 +384,12 @@ 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", message_dispatch)
mp.register_event("property-change", property_change)
+-- sent by "script_binding"
+mp.register_script_message("key-binding", dispatch_key_binding)
+
mp.msg = {
log = mp.log,
fatal = function(...) return mp.log("fatal", ...) end,