diff options
Diffstat (limited to 'options')
-rw-r--r-- | options/m_config.c | 91 | ||||
-rw-r--r-- | options/m_config.h | 31 | ||||
-rw-r--r-- | options/m_option.h | 2 | ||||
-rw-r--r-- | options/options.c | 16 |
4 files changed, 127 insertions, 13 deletions
diff --git a/options/m_config.c b/options/m_config.c index 378bed25ad..24f4b83c45 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -39,6 +39,7 @@ #include "common/global.h" #include "common/msg.h" #include "common/msg_control.h" +#include "misc/dispatch.h" #include "misc/node.h" #include "osdep/atomic.h" @@ -57,6 +58,8 @@ struct m_config_shadow { pthread_mutex_t lock; struct m_config *root; char *data; + struct m_config_cache **listeners; + int num_listeners; }; // Represents a sub-struct (OPT_SUBSTRUCT()). @@ -156,8 +159,11 @@ static void config_destroy(void *p) m_option_free(co->opt, config->shadow->data + co->shadow_offset); } - if (config->shadow) + if (config->shadow) { + // must all have been unregistered + assert(config->shadow->num_listeners == 0); pthread_mutex_destroy(&config->shadow->lock); + } } struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log, @@ -1190,6 +1196,16 @@ static bool is_group_included(struct m_config *config, int group, int parent) return false; } +static void cache_destroy(void *p) +{ + struct m_config_cache *cache = p; + + // (technically speaking, being able to call them both without anything + // breaking is a feature provided by these functions) + m_config_cache_set_wakeup_cb(cache, NULL, NULL); + m_config_cache_set_dispatch_change_cb(cache, NULL, NULL, NULL); +} + struct m_config_cache *m_config_cache_alloc(void *ta_parent, struct mpv_global *global, const struct m_sub_options *group) @@ -1198,6 +1214,7 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent, struct m_config *root = shadow->root; struct m_config_cache *cache = talloc_zero(ta_parent, struct m_config_cache); + talloc_set_destructor(cache, cache_destroy); cache->shadow = shadow; cache->shadow_config = m_config_new(cache, mp_null_log, root->size, root->defaults, root->options); @@ -1292,12 +1309,84 @@ void m_config_notify_change_co(struct m_config *config, group = g->parent_group; } + if (shadow) { + pthread_mutex_lock(&shadow->lock); + for (int n = 0; n < shadow->num_listeners; n++) { + struct m_config_cache *cache = shadow->listeners[n]; + if (cache->wakeup_cb) + cache->wakeup_cb(cache->wakeup_cb_ctx); + } + pthread_mutex_unlock(&shadow->lock); + } + if (config->option_change_callback) { config->option_change_callback(config->option_change_callback_ctx, co, changed); } } +void m_config_cache_set_wakeup_cb(struct m_config_cache *cache, + void (*cb)(void *ctx), void *cb_ctx) +{ + struct m_config_shadow *shadow = cache->shadow; + + pthread_mutex_lock(&shadow->lock); + if (cache->in_list) { + for (int n = 0; n < shadow->num_listeners; n++) { + if (shadow->listeners[n] == cache) + MP_TARRAY_REMOVE_AT(shadow->listeners, shadow->num_listeners, n); + } + if (!shadow->num_listeners) { + talloc_free(shadow->listeners); + shadow->listeners = NULL; + } + } + if (cb) { + MP_TARRAY_APPEND(NULL, shadow->listeners, shadow->num_listeners, cache); + cache->in_list = true; + cache->wakeup_cb = cb; + cache->wakeup_cb_ctx = cb_ctx; + } + pthread_mutex_unlock(&shadow->lock); +} + +static void dispatch_notify(void *p) +{ + struct m_config_cache *cache = p; + + assert(cache->wakeup_dispatch_queue); + mp_dispatch_enqueue_notify(cache->wakeup_dispatch_queue, + cache->wakeup_dispatch_cb, + cache->wakeup_dispatch_cb_ctx); +} + +void m_config_cache_set_dispatch_change_cb(struct m_config_cache *cache, + struct mp_dispatch_queue *dispatch, + void (*cb)(void *ctx), void *cb_ctx) +{ + // Remove the old one is tricky. Firts make sure no new notifications will + // come. + m_config_cache_set_wakeup_cb(cache, NULL, NULL); + // Remove any pending notifications (assume we're on the same thread as + // any potential mp_dispatch_queue_process() callers). + if (cache->wakeup_dispatch_queue) { + mp_dispatch_cancel_fn(cache->wakeup_dispatch_queue, + cache->wakeup_dispatch_cb, + cache->wakeup_dispatch_cb_ctx); + } + + cache->wakeup_dispatch_queue = NULL; + cache->wakeup_dispatch_cb = NULL; + cache->wakeup_dispatch_cb_ctx = NULL; + + if (cb) { + cache->wakeup_dispatch_queue = dispatch; + cache->wakeup_dispatch_cb = cb; + cache->wakeup_dispatch_cb_ctx = cb_ctx; + m_config_cache_set_wakeup_cb(cache, dispatch_notify, cache); + } +} + bool m_config_is_in_group(struct m_config *config, const struct m_sub_options *group, struct m_config_option *co) diff --git a/options/m_config.h b/options/m_config.h index 65145c093b..fc32ca5bf0 100644 --- a/options/m_config.h +++ b/options/m_config.h @@ -275,9 +275,20 @@ struct m_config_cache { struct m_config *shadow_config; long long ts; int group; + bool in_list; + // --- Implicitly synchronized by setting/unsetting wakeup_cb. + struct mp_dispatch_queue *wakeup_dispatch_queue; + void (*wakeup_dispatch_cb)(void *ctx); + void *wakeup_dispatch_cb_ctx; + // --- Protected by shadow->lock + void (*wakeup_cb)(void *ctx); + void *wakeup_cb_ctx; }; // Create a mirror copy from the global options. +// Keep in mind that a m_config_cache object is not thread-safe; it merely +// provides thread-safe access to the global options. All API functions for +// the same m_config_cache object must synchronized, unless otherwise noted. // ta_parent: parent for the returned allocation // global: option data source // group: the option group to return. This can be NULL for the global option @@ -287,6 +298,22 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent, struct mpv_global *global, const struct m_sub_options *group); +// If any of the options in the group possibly changes, call this callback. The +// callback must not actually access the cache or anything option related. +// Instead, it must wake up the thread that normally accesses the cache. +void m_config_cache_set_wakeup_cb(struct m_config_cache *cache, + void (*cb)(void *ctx), void *cb_ctx); + +// If any of the options in the group change, call this callback on the given +// dispatch queue. This is higher level than m_config_cache_set_wakeup_cb(), +// and you can do anything you want in the callback (assuming the dispatch +// queue is processed in the same thread that accesses m_config_cache API). +// To ensure clean shutdown, you must destroy the m_config_cache (or unset the +// callback) before the dispatch queue is destroyed. +void m_config_cache_set_dispatch_change_cb(struct m_config_cache *cache, + struct mp_dispatch_queue *dispatch, + void (*cb)(void *ctx), void *cb_ctx); + // Update the options in cache->opts to current global values. Return whether // there was an update notification at all (which may or may not indicate that // some options have changed). @@ -295,13 +322,13 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent, bool m_config_cache_update(struct m_config_cache *cache); // Like m_config_cache_alloc(), but return the struct (m_config_cache->opts) -// directly, with no way to update the config. +// directly, with no way to update the config. Basically this returns a copy +// with a snapshot of the current option values. // Warning: does currently not set the child as its own talloc root, which // means the only way to free the struct is by freeing ta_parent. void *mp_get_config_group(void *ta_parent, struct mpv_global *global, const struct m_sub_options *group); - // Read a single global option in a thread-safe way. For multiple options, // use m_config_cache. The option must exist and match the provided type (the // type is used as a sanity check only). Performs semi-expensive lookup. diff --git a/options/m_option.h b/options/m_option.h index 5feed14542..cfc5f6bb15 100644 --- a/options/m_option.h +++ b/options/m_option.h @@ -398,8 +398,6 @@ struct m_option { // certain groups of options. #define UPDATE_OPT_FIRST (1 << 7) #define UPDATE_TERM (1 << 7) // terminal options -#define UPDATE_RENDERER (1 << 8) // mainly vo_opengl options -#define UPDATE_VIDEOPOS (1 << 9) // video position (panscan etc.) #define UPDATE_OSD (1 << 10) // related to OSD rendering #define UPDATE_BUILTIN_SCRIPTS (1 << 11) // osc/ytdl #define UPDATE_IMGPAR (1 << 12) // video image params overrides diff --git a/options/options.c b/options/options.c index 2bd7bf869d..b35dd9b265 100644 --- a/options/options.c +++ b/options/options.c @@ -146,20 +146,20 @@ static const m_option_t mp_vo_opt_list[] = { OPT_FLAG("fullscreen", fullscreen, 0), OPT_ALIAS("fs", "fullscreen"), OPT_FLAG("native-keyrepeat", native_keyrepeat, 0), - OPT_FLOATRANGE("panscan", panscan, UPDATE_VIDEOPOS, 0.0, 1.0), - OPT_FLOATRANGE("video-zoom", zoom, UPDATE_VIDEOPOS, -20.0, 20.0), - OPT_FLOATRANGE("video-pan-x", pan_x, UPDATE_VIDEOPOS, -3.0, 3.0), - OPT_FLOATRANGE("video-pan-y", pan_y, UPDATE_VIDEOPOS, -3.0, 3.0), - OPT_FLOATRANGE("video-align-x", align_x, UPDATE_VIDEOPOS, -1.0, 1.0), - OPT_FLOATRANGE("video-align-y", align_y, UPDATE_VIDEOPOS, -1.0, 1.0), - OPT_CHOICE("video-unscaled", unscaled, UPDATE_VIDEOPOS, + OPT_FLOATRANGE("panscan", panscan, 0, 0.0, 1.0), + OPT_FLOATRANGE("video-zoom", zoom, 0, -20.0, 20.0), + OPT_FLOATRANGE("video-pan-x", pan_x, 0, -3.0, 3.0), + OPT_FLOATRANGE("video-pan-y", pan_y, 0, -3.0, 3.0), + OPT_FLOATRANGE("video-align-x", align_x, 0, -1.0, 1.0), + OPT_FLOATRANGE("video-align-y", align_y, 0, -1.0, 1.0), + OPT_CHOICE("video-unscaled", unscaled, 0, ({"no", 0}, {"yes", 1}, {"downscale-big", 2})), OPT_INT64("wid", WinID, 0), OPT_CHOICE_OR_INT("screen", screen_id, 0, 0, 32, ({"default", -1})), OPT_CHOICE_OR_INT("fs-screen", fsscreen_id, 0, 0, 32, ({"all", -2}, {"current", -1})), - OPT_FLAG("keepaspect", keepaspect, UPDATE_VIDEOPOS), + OPT_FLAG("keepaspect", keepaspect, 0), OPT_FLAG("keepaspect-window", keepaspect_window, 0), OPT_FLAG("hidpi-window-scale", hidpi_window_scale, 0), OPT_FLAG("native-fs", native_fs, 0), |