mirror of https://github.com/mpv-player/mpv
client API: add a first class hook API, and deprecate old API
As it turns out, there are multiple libmpv users who saw a need to use the hook API. The API is kind of shitty and was never meant to be actually public (it was mostly a hack for the ytdl script). Introduce a proper API and deprecate the old one. The old one will probably continue to work for a few releases, but will be removed eventually. There are some slight changes to the old API, but if a user followed the manual properly, it won't break. Mostly untested. Appears to work with ytdl_hook.
This commit is contained in:
parent
6d7cfdfae5
commit
f60826c3a1
|
@ -41,6 +41,9 @@ API changes
|
|||
- deprecate mpv_get_wakeup_pipe(). It's complex, but easy to replace
|
||||
using normal API (just set a wakeup callback to a function which
|
||||
writes to a pipe).
|
||||
- add a 1st class hook API, which replaces the hacky mpv_command()
|
||||
based one. The old API is deprecated and will be removed soon. The
|
||||
old API was never meant to be stable, while the new API is.
|
||||
1.29 - the behavior of mpv_terminate_destroy() and mpv_detach_destroy()
|
||||
changes subtly (see documentation in the header file). In particular,
|
||||
mpv_detach_destroy() will not leave the player running in all
|
||||
|
|
|
@ -82,6 +82,8 @@ Interface changes
|
|||
- change vf_vavpp default to use the best deinterlace algorithm by default
|
||||
- remove a compatibility hack that allowed CLI aliases to be set as property
|
||||
(such as "sub-file"), deprecated in mpv 0.26.0
|
||||
- deprecate the old command based hook API, and introduce a proper C API
|
||||
(the high level Lua API for this does not change)
|
||||
--- mpv 0.28.0 ---
|
||||
- rename --hwdec=mediacodec option to mediacodec-copy, to reflect
|
||||
conventions followed by other hardware video decoding APIs
|
||||
|
|
|
@ -762,40 +762,8 @@ and obscure way to handle events that require stricter coordination. There are
|
|||
no API stability guarantees made. Not following the protocol exactly can make
|
||||
the player freeze randomly. Basically, nobody should use this API.
|
||||
|
||||
There are two special commands involved. Also, the client must listen for
|
||||
client messages (``MPV_EVENT_CLIENT_MESSAGE`` in the C API).
|
||||
|
||||
``hook-add <hook-name> <id> <priority>``
|
||||
Subscribe to the hook identified by the first argument (basically, the
|
||||
name of event). The ``id`` argument is an arbitrary integer chosen by the
|
||||
user. ``priority`` is used to sort all hook handlers globally across all
|
||||
clients. Each client can register multiple hook handlers (even for the
|
||||
same hook-name). Once the hook is registered, it cannot be unregistered.
|
||||
|
||||
When a specific event happens, all registered handlers are run serially.
|
||||
This uses a protocol every client has to follow explicitly. When a hook
|
||||
handler is run, a client message (``MPV_EVENT_CLIENT_MESSAGE``) is sent to
|
||||
the client which registered the hook. This message has the following
|
||||
arguments:
|
||||
|
||||
1. the string ``hook_run``
|
||||
2. the ``id`` argument the hook was registered with as string (this can be
|
||||
used to correctly handle multiple hooks registered by the same client,
|
||||
as long as the ``id`` argument is unique in the client)
|
||||
3. something undefined, used by the hook mechanism to track hook execution
|
||||
(currently, it's the hook-name, but this might change without warning)
|
||||
|
||||
Upon receiving this message, the client can handle the event. While doing
|
||||
this, the player core will still react to requests, but playback will
|
||||
typically be stopped.
|
||||
|
||||
When the client is done, it must continue the core's hook execution by
|
||||
running the ``hook-ack`` command.
|
||||
|
||||
``hook-ack <string>``
|
||||
Run the next hook in the global chain of hooks. The argument is the 3rd
|
||||
argument of the client message that starts hook execution for the
|
||||
current client.
|
||||
The C API is described in the header files. The Lua API is described in the
|
||||
Lua section.
|
||||
|
||||
The following hooks are currently defined:
|
||||
|
||||
|
@ -830,6 +798,47 @@ The following hooks are currently defined:
|
|||
Run before closing a file, and before actually uninitializing
|
||||
everything. It's not possible to resume playback in this state.
|
||||
|
||||
Legacy hook API
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. warning::
|
||||
|
||||
The legacy API is deprecated and will be removed soon.
|
||||
|
||||
There are two special commands involved. Also, the client must listen for
|
||||
client messages (``MPV_EVENT_CLIENT_MESSAGE`` in the C API).
|
||||
|
||||
``hook-add <hook-name> <id> <priority>``
|
||||
Subscribe to the hook identified by the first argument (basically, the
|
||||
name of event). The ``id`` argument is an arbitrary integer chosen by the
|
||||
user. ``priority`` is used to sort all hook handlers globally across all
|
||||
clients. Each client can register multiple hook handlers (even for the
|
||||
same hook-name). Once the hook is registered, it cannot be unregistered.
|
||||
|
||||
When a specific event happens, all registered handlers are run serially.
|
||||
This uses a protocol every client has to follow explicitly. When a hook
|
||||
handler is run, a client message (``MPV_EVENT_CLIENT_MESSAGE``) is sent to
|
||||
the client which registered the hook. This message has the following
|
||||
arguments:
|
||||
|
||||
1. the string ``hook_run``
|
||||
2. the ``id`` argument the hook was registered with as string (this can be
|
||||
used to correctly handle multiple hooks registered by the same client,
|
||||
as long as the ``id`` argument is unique in the client)
|
||||
3. something undefined, used by the hook mechanism to track hook execution
|
||||
|
||||
Upon receiving this message, the client can handle the event. While doing
|
||||
this, the player core will still react to requests, but playback will
|
||||
typically be stopped.
|
||||
|
||||
When the client is done, it must continue the core's hook execution by
|
||||
running the ``hook-ack`` command.
|
||||
|
||||
``hook-ack <string>``
|
||||
Run the next hook in the global chain of hooks. The argument is the 3rd
|
||||
argument of the client message that starts hook execution for the
|
||||
current client.
|
||||
|
||||
Input Command Prefixes
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -209,7 +209,7 @@ const struct mp_cmd_def mp_cmds[] = {
|
|||
{ MP_CMD_WRITE_WATCH_LATER_CONFIG, "write-watch-later-config", },
|
||||
|
||||
{ MP_CMD_HOOK_ADD, "hook-add", { ARG_STRING, ARG_INT, ARG_INT } },
|
||||
{ MP_CMD_HOOK_ACK, "hook-ack", { ARG_STRING } },
|
||||
{ MP_CMD_HOOK_ACK, "hook-ack", { ARG_INT } },
|
||||
|
||||
{ MP_CMD_MOUSE, "mouse", {
|
||||
ARG_INT, ARG_INT, // coordinate (x, y)
|
||||
|
|
|
@ -1133,6 +1133,9 @@ int mpv_get_property_async(mpv_handle *ctx, uint64_t reply_userdata,
|
|||
* property yourself. Try to avoid endless feedback loops, which could happen
|
||||
* if you react to the change notifications triggered by your own change.
|
||||
*
|
||||
* Only the mpv_handle on which this was called will receive the property
|
||||
* change events, or can unobserve them.
|
||||
*
|
||||
* @param reply_userdata This will be used for the mpv_event.reply_userdata
|
||||
* field for the received MPV_EVENT_PROPERTY_CHANGE
|
||||
* events. (Also see section about asynchronous calls,
|
||||
|
@ -1350,7 +1353,14 @@ typedef enum mpv_event_id {
|
|||
* Event delivery will continue normally once this event was returned
|
||||
* (this forces the client to empty the queue completely).
|
||||
*/
|
||||
MPV_EVENT_QUEUE_OVERFLOW = 24
|
||||
MPV_EVENT_QUEUE_OVERFLOW = 24,
|
||||
/**
|
||||
* Triggered if a hook handler was registered with mpv_hook_add(), and the
|
||||
* hook is invoked. If you receive this, you must handle it, and continue
|
||||
* the hook with mpv_hook_continue().
|
||||
* See also mpv_event and mpv_event_hook.
|
||||
*/
|
||||
MPV_EVENT_HOOK = 25,
|
||||
// Internal note: adjust INTERNAL_EVENT_BASE when adding new events.
|
||||
} mpv_event_id;
|
||||
|
||||
|
@ -1514,6 +1524,17 @@ typedef struct mpv_event_client_message {
|
|||
const char **args;
|
||||
} mpv_event_client_message;
|
||||
|
||||
typedef struct mpv_event_hook {
|
||||
/**
|
||||
* The hook name as passed to mpv_hook_add().
|
||||
*/
|
||||
const char *name;
|
||||
/**
|
||||
* Internal ID that must be passed to mpv_hook_continue().
|
||||
*/
|
||||
uint64_t id;
|
||||
} mpv_event_hook;
|
||||
|
||||
typedef struct mpv_event {
|
||||
/**
|
||||
* One of mpv_event. Keep in mind that later ABI compatible releases might
|
||||
|
@ -1682,6 +1703,62 @@ void mpv_set_wakeup_callback(mpv_handle *ctx, void (*cb)(void *d), void *d);
|
|||
*/
|
||||
void mpv_wait_async_requests(mpv_handle *ctx);
|
||||
|
||||
/**
|
||||
* A hook is like a synchronous event that blocks the player. You register
|
||||
* a hook handler with this function. You will get an event, which you need
|
||||
* to handle, and once things are ready, you can let the player continue with
|
||||
* mpv_hook_continue().
|
||||
*
|
||||
* Currently, hooks can't be removed explicitly. But they will be implicitly
|
||||
* removed if the mpv_handle it was registered with is destroyed. This also
|
||||
* continues the hook if it was being handled by the destroyed mpv_handle (but
|
||||
* this should be avoided, as it might mess up order of hook execution).
|
||||
*
|
||||
* Hook handlers are ordered globally by priority and order of registration.
|
||||
* Handlers for the same hook with same priority are invoked in order of
|
||||
* registration (the handler registered first is run first). Handlers with
|
||||
* lower priority are run first (which seems backward).
|
||||
*
|
||||
* See the "Hooks" section in the manpage to see which hooks are currently
|
||||
* defined.
|
||||
*
|
||||
* Some hooks might be reentrant (so you get multiple MPV_EVENT_HOOK for the
|
||||
* same hook). If this can happen for a specific hook type, it will be
|
||||
* explicitly documented in the manpage.
|
||||
*
|
||||
* Only the mpv_handle on which this was called will receive the hook events,
|
||||
* or can "continue" them.
|
||||
*
|
||||
* @param reply_userdata This will be used for the mpv_event.reply_userdata
|
||||
* field for the received MPV_EVENT_HOOK events.
|
||||
* If you have no use for this, pass 0.
|
||||
* @param name The hook name. This should be one of the documented names. But
|
||||
* if the name is unknown, the hook event will simply be never
|
||||
* raised.
|
||||
* @param priority See remarks above. Use 0 as a neutral default.
|
||||
* @return error code (usually fails only on OOM)
|
||||
*/
|
||||
int mpv_hook_add(mpv_handle *ctx, uint64_t reply_userdata,
|
||||
const char *name, int priority);
|
||||
|
||||
/**
|
||||
* Respond to a MPV_EVENT_HOOK event. You must call this after you have handled
|
||||
* the event. There is no way to "cancel" or "stop" the hook.
|
||||
*
|
||||
* Calling this will will typically unblock the player for whatever the hook
|
||||
* is responsible for (e.g. for the "on_load" hook it lets it continue
|
||||
* playback).
|
||||
*
|
||||
* It is explicitly undefined behavior to call this more than once for each
|
||||
* MPV_EVENT_HOOK, to pass an incorrect ID, or to call this on a mpv_handle
|
||||
* different from the one that registered the handler and received the event.
|
||||
*
|
||||
* @param id This must be the value of the mpv_event_hook.id field for the
|
||||
* corresponding MPV_EVENT_HOOK.
|
||||
* @return error code
|
||||
*/
|
||||
int mpv_hook_continue(mpv_handle *ctx, uint64_t id);
|
||||
|
||||
#if MPV_ENABLE_DEPRECATED
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,8 @@ mpv_get_property_string
|
|||
mpv_get_sub_api
|
||||
mpv_get_time_us
|
||||
mpv_get_wakeup_pipe
|
||||
mpv_hook_add
|
||||
mpv_hook_continue
|
||||
mpv_initialize
|
||||
mpv_load_config_file
|
||||
mpv_observe_property
|
||||
|
|
|
@ -728,7 +728,7 @@ void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data)
|
|||
|
||||
// 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)
|
||||
uint64_t reply_userdata, int event, void *data)
|
||||
{
|
||||
if (!client_name) {
|
||||
mp_client_broadcast_event(mpctx, event, data);
|
||||
|
@ -742,6 +742,7 @@ int mp_client_send_event(struct MPContext *mpctx, const char *client_name,
|
|||
struct mpv_event event_data = {
|
||||
.event_id = event,
|
||||
.data = data,
|
||||
.reply_userdata = reply_userdata,
|
||||
};
|
||||
|
||||
pthread_mutex_lock(&clients->lock);
|
||||
|
@ -773,7 +774,7 @@ int mp_client_send_event_dup(struct MPContext *mpctx, const char *client_name,
|
|||
};
|
||||
|
||||
dup_event_data(&event_data);
|
||||
return mp_client_send_event(mpctx, client_name, event, event_data.data);
|
||||
return mp_client_send_event(mpctx, client_name, 0, event, event_data.data);
|
||||
}
|
||||
|
||||
int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable)
|
||||
|
@ -1558,6 +1559,23 @@ static bool gen_property_change_event(struct mpv_handle *ctx)
|
|||
return false;
|
||||
}
|
||||
|
||||
int mpv_hook_add(mpv_handle *ctx, uint64_t reply_userdata,
|
||||
const char *name, int priority)
|
||||
{
|
||||
lock_core(ctx);
|
||||
mp_hook_add(ctx->mpctx, ctx->name, name, reply_userdata, priority, false);
|
||||
unlock_core(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mpv_hook_continue(mpv_handle *ctx, uint64_t id)
|
||||
{
|
||||
lock_core(ctx);
|
||||
int r = mp_hook_continue(ctx->mpctx, ctx->name, id);
|
||||
unlock_core(ctx);
|
||||
return r;
|
||||
}
|
||||
|
||||
int mpv_load_config_file(mpv_handle *ctx, const char *filename)
|
||||
{
|
||||
int flags = ctx->mpctx->initialized ? M_SETOPT_RUNTIME : 0;
|
||||
|
@ -1708,6 +1726,7 @@ static const char *const event_table[] = {
|
|||
[MPV_EVENT_PROPERTY_CHANGE] = "property-change",
|
||||
[MPV_EVENT_CHAPTER_CHANGE] = "chapter-change",
|
||||
[MPV_EVENT_QUEUE_OVERFLOW] = "event-queue-overflow",
|
||||
[MPV_EVENT_HOOK] = "hook",
|
||||
};
|
||||
|
||||
const char *mpv_event_name(mpv_event_id event)
|
||||
|
|
|
@ -25,7 +25,7 @@ bool mp_clients_all_initialized(struct MPContext *mpctx);
|
|||
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);
|
||||
uint64_t reply_userdata, 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);
|
||||
|
|
137
player/command.c
137
player/command.c
|
@ -115,9 +115,10 @@ struct overlay {
|
|||
struct hook_handler {
|
||||
char *client; // client API user name
|
||||
char *type; // kind of hook, e.g. "on_load"
|
||||
char *user_id; // numeric user-chosen ID, printed as string
|
||||
uint64_t user_id; // user-chosen ID
|
||||
int priority; // priority for global hook order
|
||||
int64_t seq; // unique ID (also age -> fixed order for equal priorities)
|
||||
int64_t seq; // unique ID, != 0, also for fixed order on equal priorities
|
||||
bool legacy; // old cmd based hook API
|
||||
bool active; // hook is currently in progress (only 1 at a time for now)
|
||||
};
|
||||
|
||||
|
@ -137,12 +138,17 @@ static int set_filters(struct MPContext *mpctx, enum stream_type mediatype,
|
|||
static int mp_property_do_silent(const char *name, int action, void *val,
|
||||
struct MPContext *ctx);
|
||||
|
||||
static void hook_remove(struct MPContext *mpctx, int index)
|
||||
static void hook_remove(struct MPContext *mpctx, struct hook_handler *h)
|
||||
{
|
||||
struct command_ctx *cmd = mpctx->command_ctx;
|
||||
assert(index >= 0 && index < cmd->num_hooks);
|
||||
talloc_free(cmd->hooks[index]);
|
||||
MP_TARRAY_REMOVE_AT(cmd->hooks, cmd->num_hooks, index);
|
||||
for (int n = 0; n < cmd->num_hooks; n++) {
|
||||
if (cmd->hooks[n] == h) {
|
||||
talloc_free(cmd->hooks[n]);
|
||||
MP_TARRAY_REMOVE_AT(cmd->hooks, cmd->num_hooks, n);
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
bool mp_hook_test_completion(struct MPContext *mpctx, char *type)
|
||||
|
@ -152,7 +158,8 @@ bool mp_hook_test_completion(struct MPContext *mpctx, char *type)
|
|||
struct hook_handler *h = cmd->hooks[n];
|
||||
if (h->active && strcmp(h->type, type) == 0) {
|
||||
if (!mp_client_exists(mpctx, h->client)) {
|
||||
hook_remove(mpctx, n);
|
||||
MP_WARN(mpctx, "client removed during hook handling\n");
|
||||
hook_remove(mpctx, h);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
|
@ -161,49 +168,81 @@ bool mp_hook_test_completion(struct MPContext *mpctx, char *type)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool send_hook_msg(struct MPContext *mpctx, struct hook_handler *h,
|
||||
char *cmd)
|
||||
static int invoke_hook_handler(struct MPContext *mpctx, struct hook_handler *h)
|
||||
{
|
||||
mpv_event_client_message *m = talloc_ptrtype(NULL, m);
|
||||
*m = (mpv_event_client_message){0};
|
||||
MP_TARRAY_APPEND(m, m->args, m->num_args, cmd);
|
||||
MP_TARRAY_APPEND(m, m->args, m->num_args, talloc_strdup(m, h->user_id));
|
||||
MP_TARRAY_APPEND(m, m->args, m->num_args, talloc_strdup(m, h->type));
|
||||
bool r =
|
||||
mp_client_send_event(mpctx, h->client, MPV_EVENT_CLIENT_MESSAGE, m) >= 0;
|
||||
if (!r)
|
||||
MP_WARN(mpctx, "Sending hook command failed.\n");
|
||||
MP_VERBOSE(mpctx, "Running hook: %s/%s\n", h->client, h->type);
|
||||
h->active = true;
|
||||
|
||||
uint64_t reply_id = 0;
|
||||
void *data;
|
||||
int msg;
|
||||
if (h->legacy) {
|
||||
mpv_event_client_message *m = talloc_ptrtype(NULL, m);
|
||||
*m = (mpv_event_client_message){0};
|
||||
MP_TARRAY_APPEND(m, m->args, m->num_args, "hook_run");
|
||||
MP_TARRAY_APPEND(m, m->args, m->num_args,
|
||||
talloc_asprintf(m, "%llu", (long long)h->user_id));
|
||||
MP_TARRAY_APPEND(m, m->args, m->num_args,
|
||||
talloc_asprintf(m, "%llu", (long long)h->seq));
|
||||
data = m;
|
||||
msg = MPV_EVENT_CLIENT_MESSAGE;
|
||||
} else {
|
||||
mpv_event_hook *m = talloc_ptrtype(NULL, m);
|
||||
*m = (mpv_event_hook){
|
||||
.name = talloc_strdup(m, h->type),
|
||||
.id = h->seq,
|
||||
},
|
||||
reply_id = h->user_id;
|
||||
data = m;
|
||||
msg = MPV_EVENT_HOOK;
|
||||
}
|
||||
int r = mp_client_send_event(mpctx, h->client, reply_id, msg, data);
|
||||
if (r < 0) {
|
||||
MP_WARN(mpctx, "Sending hook command failed. Removing hook.\n");
|
||||
hook_remove(mpctx, h);
|
||||
mp_wakeup_core(mpctx); // repeat next iteration to finish
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// client==NULL means start the hook chain
|
||||
void mp_hook_run(struct MPContext *mpctx, char *client, char *type)
|
||||
static int run_next_hook_handler(struct MPContext *mpctx, char *type, int index)
|
||||
{
|
||||
struct command_ctx *cmd = mpctx->command_ctx;
|
||||
bool found_current = !client;
|
||||
int index = -1;
|
||||
|
||||
for (int n = index; n < cmd->num_hooks; n++) {
|
||||
struct hook_handler *h = cmd->hooks[n];
|
||||
if (strcmp(h->type, type) == 0)
|
||||
return invoke_hook_handler(mpctx, h);
|
||||
}
|
||||
|
||||
mp_wakeup_core(mpctx); // finished hook
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mp_hook_run(struct MPContext *mpctx, char *type)
|
||||
{
|
||||
while (run_next_hook_handler(mpctx, type, 0) < 0) {
|
||||
// We can repeat this until all broken clients have been removed, and
|
||||
// hook processing is successfully started.
|
||||
}
|
||||
}
|
||||
|
||||
int mp_hook_continue(struct MPContext *mpctx, char *client, uint64_t id)
|
||||
{
|
||||
struct command_ctx *cmd = mpctx->command_ctx;
|
||||
|
||||
for (int n = 0; n < cmd->num_hooks; n++) {
|
||||
struct hook_handler *h = cmd->hooks[n];
|
||||
if (!found_current) {
|
||||
if (h->active && strcmp(h->type, type) == 0) {
|
||||
h->active = false;
|
||||
found_current = true;
|
||||
mp_wakeup_core(mpctx);
|
||||
}
|
||||
} else if (strcmp(h->type, type) == 0) {
|
||||
index = n;
|
||||
break;
|
||||
if (strcmp(h->client, client) == 0 && h->seq == id) {
|
||||
if (!h->active)
|
||||
break;
|
||||
h->active = false;
|
||||
return run_next_hook_handler(mpctx, h->type, n + 1);
|
||||
}
|
||||
}
|
||||
if (index < 0)
|
||||
return;
|
||||
struct hook_handler *next = cmd->hooks[index];
|
||||
MP_VERBOSE(mpctx, "Running hook: %s/%s\n", next->client, type);
|
||||
next->active = true;
|
||||
if (!send_hook_msg(mpctx, next, "hook_run")) {
|
||||
hook_remove(mpctx, index);
|
||||
mp_wakeup_core(mpctx); // repeat next iteration to finish
|
||||
}
|
||||
|
||||
MP_ERR(mpctx, "invalid hook API usage\n");
|
||||
return MPV_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
static int compare_hook(const void *pa, const void *pb)
|
||||
|
@ -215,18 +254,22 @@ static int compare_hook(const void *pa, const void *pb)
|
|||
return (*h1)->seq - (*h2)->seq;
|
||||
}
|
||||
|
||||
static void mp_hook_add(struct MPContext *mpctx, char *client, char *name,
|
||||
int id, int pri)
|
||||
void mp_hook_add(struct MPContext *mpctx, const char *client, const char *name,
|
||||
uint64_t user_id, int pri, bool legacy)
|
||||
{
|
||||
if (legacy)
|
||||
MP_WARN(mpctx, "The old hook API is deprecated! Use the libmpv API.\n");
|
||||
|
||||
struct command_ctx *cmd = mpctx->command_ctx;
|
||||
struct hook_handler *h = talloc_ptrtype(cmd, h);
|
||||
int64_t seq = cmd->hook_seq++;
|
||||
int64_t seq = ++cmd->hook_seq;
|
||||
*h = (struct hook_handler){
|
||||
.client = talloc_strdup(h, client),
|
||||
.type = talloc_strdup(h, name),
|
||||
.user_id = talloc_asprintf(h, "%d", id),
|
||||
.user_id = user_id,
|
||||
.priority = pri,
|
||||
.seq = seq,
|
||||
.legacy = legacy,
|
||||
};
|
||||
MP_TARRAY_APPEND(cmd, cmd->hooks, cmd->num_hooks, h);
|
||||
qsort(cmd->hooks, cmd->num_hooks, sizeof(cmd->hooks[0]), compare_hook);
|
||||
|
@ -5409,7 +5452,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
|||
MP_TARRAY_APPEND(event, event->args, event->num_args,
|
||||
talloc_strdup(event, cmd->args[n].v.s));
|
||||
}
|
||||
if (mp_client_send_event(mpctx, cmd->args[0].v.s,
|
||||
if (mp_client_send_event(mpctx, cmd->args[0].v.s, 0,
|
||||
MPV_EVENT_CLIENT_MESSAGE, event) < 0)
|
||||
{
|
||||
MP_VERBOSE(mpctx, "Can't find script '%s' for %s.\n",
|
||||
|
@ -5459,14 +5502,14 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
|||
return -1;
|
||||
}
|
||||
mp_hook_add(mpctx, cmd->sender, cmd->args[0].v.s, cmd->args[1].v.i,
|
||||
cmd->args[2].v.i);
|
||||
cmd->args[2].v.i, true);
|
||||
break;
|
||||
case MP_CMD_HOOK_ACK:
|
||||
if (!cmd->sender) {
|
||||
MP_ERR(mpctx, "Can be used from client API only.\n");
|
||||
return -1;
|
||||
}
|
||||
mp_hook_run(mpctx, cmd->sender, cmd->args[0].v.s);
|
||||
mp_hook_continue(mpctx, cmd->sender, cmd->args[0].v.i);
|
||||
break;
|
||||
|
||||
case MP_CMD_MOUSE: {
|
||||
|
|
|
@ -50,7 +50,7 @@ uint64_t mp_get_property_event_mask(const char *name);
|
|||
enum {
|
||||
// Must start with the first unused positive value in enum mpv_event_id
|
||||
// MPV_EVENT_* and MP_EVENT_* must not overlap.
|
||||
INTERNAL_EVENT_BASE = 25,
|
||||
INTERNAL_EVENT_BASE = 26,
|
||||
MP_EVENT_CHANGE_ALL,
|
||||
MP_EVENT_CACHE_UPDATE,
|
||||
MP_EVENT_WIN_RESIZE,
|
||||
|
@ -61,7 +61,10 @@ enum {
|
|||
};
|
||||
|
||||
bool mp_hook_test_completion(struct MPContext *mpctx, char *type);
|
||||
void mp_hook_run(struct MPContext *mpctx, char *client, char *type);
|
||||
void mp_hook_run(struct MPContext *mpctx, char *type);
|
||||
int mp_hook_continue(struct MPContext *mpctx, char *client, uint64_t id);
|
||||
void mp_hook_add(struct MPContext *mpctx, const char *client, const char *name,
|
||||
uint64_t user_id, int pri, bool legacy);
|
||||
|
||||
void mark_seek(struct MPContext *mpctx);
|
||||
|
||||
|
|
|
@ -753,7 +753,7 @@ static void transfer_playlist(struct MPContext *mpctx, struct playlist *pl)
|
|||
static int process_open_hooks(struct MPContext *mpctx, char *name)
|
||||
{
|
||||
|
||||
mp_hook_run(mpctx, NULL, name);
|
||||
mp_hook_run(mpctx, name);
|
||||
|
||||
while (!mp_hook_test_completion(mpctx, name)) {
|
||||
mp_idle(mpctx);
|
||||
|
@ -770,7 +770,7 @@ static int process_open_hooks(struct MPContext *mpctx, char *name)
|
|||
|
||||
static int process_preloaded_hooks(struct MPContext *mpctx)
|
||||
{
|
||||
mp_hook_run(mpctx, NULL, "on_preloaded");
|
||||
mp_hook_run(mpctx, "on_preloaded");
|
||||
|
||||
while (!mp_hook_test_completion(mpctx, "on_preloaded")) {
|
||||
mp_idle(mpctx);
|
||||
|
@ -783,7 +783,7 @@ static int process_preloaded_hooks(struct MPContext *mpctx)
|
|||
|
||||
static void process_unload_hooks(struct MPContext *mpctx)
|
||||
{
|
||||
mp_hook_run(mpctx, NULL, "on_unload");
|
||||
mp_hook_run(mpctx, "on_unload");
|
||||
|
||||
while (!mp_hook_test_completion(mpctx, "on_unload"))
|
||||
mp_idle(mpctx);
|
||||
|
|
24
player/lua.c
24
player/lua.c
|
@ -556,6 +556,12 @@ static int script_wait_event(lua_State *L)
|
|||
lua_setfield(L, -2, "data");
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_HOOK: {
|
||||
mpv_event_hook *hook = event->data;
|
||||
lua_pushinteger(L, hook->id);
|
||||
lua_setfield(L, -2, "hook_id");
|
||||
break;
|
||||
}
|
||||
default: ;
|
||||
}
|
||||
|
||||
|
@ -1046,6 +1052,22 @@ static int script_get_wakeup_pipe(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int script_raw_hook_add(lua_State *L)
|
||||
{
|
||||
struct script_ctx *ctx = get_ctx(L);
|
||||
uint64_t ud = luaL_checkinteger(L, 1);
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
int pri = luaL_checkinteger(L, 3);
|
||||
return check_error(L, mpv_hook_add(ctx->client, ud, name, pri));
|
||||
}
|
||||
|
||||
static int script_raw_hook_continue(lua_State *L)
|
||||
{
|
||||
struct script_ctx *ctx = get_ctx(L);
|
||||
lua_Integer id = luaL_checkinteger(L, 1);
|
||||
return check_error(L, mpv_hook_continue(ctx->client, id));
|
||||
}
|
||||
|
||||
static int script_readdir(lua_State *L)
|
||||
{
|
||||
// 0 1 2 3
|
||||
|
@ -1335,6 +1357,8 @@ static const struct fn_entry main_fns[] = {
|
|||
FN_ENTRY(format_time),
|
||||
FN_ENTRY(enable_messages),
|
||||
FN_ENTRY(get_wakeup_pipe),
|
||||
FN_ENTRY(raw_hook_add),
|
||||
FN_ENTRY(raw_hook_continue),
|
||||
{0}
|
||||
};
|
||||
|
||||
|
|
|
@ -511,24 +511,21 @@ function mp.osd_message(text, duration)
|
|||
end
|
||||
|
||||
local hook_table = {}
|
||||
local hook_registered = false
|
||||
|
||||
local function hook_run(id, cont)
|
||||
local fn = hook_table[tonumber(id)]
|
||||
mp.register_event("hook", function(ev)
|
||||
local fn = hook_table[tonumber(ev.id)]
|
||||
if fn then
|
||||
fn()
|
||||
end
|
||||
mp.commandv("hook-ack", cont)
|
||||
end
|
||||
mp.raw_hook_continue(ev.hook_id)
|
||||
end)
|
||||
|
||||
function mp.add_hook(name, pri, cb)
|
||||
if not hook_registered then
|
||||
mp.register_script_message("hook_run", hook_run)
|
||||
hook_registered = true
|
||||
end
|
||||
local id = #hook_table + 1
|
||||
hook_table[id] = cb
|
||||
mp.commandv("hook-add", name, id, pri)
|
||||
-- The C API suggests using 0 for a neutral priority, but lua.rst suggests
|
||||
-- 50 (?), so whatever.
|
||||
mp.raw_hook_add(id, name, pri - 50)
|
||||
end
|
||||
|
||||
local mp_utils = package.loaded["mp.utils"]
|
||||
|
|
|
@ -207,7 +207,8 @@ static void load_builtin_script(struct MPContext *mpctx, bool enable,
|
|||
// terminated, or re-enabling the script could be racy (because it'd
|
||||
// recognize a still-terminating script as "loaded").
|
||||
while (mp_client_exists(mpctx, name)) {
|
||||
if (mp_client_send_event(mpctx, name, MPV_EVENT_SHUTDOWN, NULL) < 0)
|
||||
if (mp_client_send_event(mpctx, name, 0, MPV_EVENT_SHUTDOWN,
|
||||
NULL) < 0)
|
||||
break;
|
||||
mp_idle(mpctx);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue