summaryrefslogtreecommitdiffstats
path: root/player
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2018-03-23 16:24:49 +0100
committerKevin Mitchell <kevmitch@gmail.com>2018-03-26 23:02:23 -0700
commitf60826c3a14ba3b49077f17e5364b7347f9b468a (patch)
tree285f39963a9f978939743b12527539e1ec8a3f54 /player
parent6d7cfdfae582353e1f10797bb2c587e6ada0aed7 (diff)
downloadmpv-f60826c3a14ba3b49077f17e5364b7347f9b468a.tar.bz2
mpv-f60826c3a14ba3b49077f17e5364b7347f9b468a.tar.xz
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.
Diffstat (limited to 'player')
-rw-r--r--player/client.c23
-rw-r--r--player/client.h2
-rw-r--r--player/command.c139
-rw-r--r--player/command.h7
-rw-r--r--player/loadfile.c6
-rw-r--r--player/lua.c24
-rw-r--r--player/lua/defaults.lua17
-rw-r--r--player/scripting.c3
8 files changed, 154 insertions, 67 deletions
diff --git a/player/client.c b/player/client.c
index c6803806c9..1fe38881ad 100644
--- a/player/client.c
+++ b/player/client.c
@@ -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)
diff --git a/player/client.h b/player/client.h
index 9ecbe2ed35..b1c2ffc500 100644
--- a/player/client.h
+++ b/player/client.h
@@ -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);
diff --git a/player/command.c b/player/command.c
index 572930e69f..da5a7e39f5 100644
--- a/player/command.c
+++ b/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)
-{
- 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");
+static int invoke_hook_handler(struct MPContext *mpctx, struct hook_handler *h)
+{
+ 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;
+
+ 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;
- bool found_current = !client;
- int index = -1;
+
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: {
diff --git a/player/command.h b/player/command.h
index 478bc8c737..2a102ddfbe 100644
--- a/player/command.h
+++ b/player/command.h
@@ -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);
diff --git a/player/loadfile.c b/player/loadfile.c
index 5c741369c0..32f41213fd 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -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);
diff --git a/player/lua.c b/player/lua.c
index 778830976c..98dcfee5b2 100644
--- a/player/lua.c
+++ b/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}
};
diff --git a/player/lua/defaults.lua b/player/lua/defaults.lua
index 32dfed9948..d5bb194c50 100644
--- a/player/lua/defaults.lua
+++ b/player/lua/defaults.lua
@@ -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"]
diff --git a/player/scripting.c b/player/scripting.c
index 9d2cd97de5..b98588d948 100644
--- a/player/scripting.c
+++ b/player/scripting.c
@@ -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);
}