summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-11-26 22:18:56 +0100
committerwm4 <wm4@nowhere>2019-11-26 22:18:56 +0100
commit01afc99def5d7381999e27720caf2beb785285f2 (patch)
treeb48dbbf6e4f3f1086e1d7d5d8b813782209d7738 /video/out
parent7c6570402bb203a1f1deb5b76807a9c021f47e82 (diff)
downloadmpv-01afc99def5d7381999e27720caf2beb785285f2.tar.bz2
mpv-01afc99def5d7381999e27720caf2beb785285f2.tar.xz
player, vo: add tons of glue code to cover up past mistakes
Every VO backend should use this for everything. It's not the ideal solution, but it'll survive the next transition, and in addition is useful to handle interaction with the window system, like fullscreen and also properties like maximized/minimized typically need it.
Diffstat (limited to 'video/out')
-rw-r--r--video/out/vo.c34
-rw-r--r--video/out/vo.h22
-rw-r--r--video/out/win_state.c180
-rw-r--r--video/out/win_state.h57
4 files changed, 287 insertions, 6 deletions
diff --git a/video/out/vo.c b/video/out/vo.c
index e28026cab5..90f33e60e4 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -45,6 +45,7 @@
#include "sub/osd.h"
#include "osdep/io.h"
#include "osdep/threads.h"
+#include "win_state.h"
extern const struct vo_driver video_out_mediacodec_embed;
extern const struct vo_driver video_out_x11;
@@ -162,6 +163,8 @@ struct vo_internal {
double display_fps;
double reported_display_fps;
+
+ struct vo_win_state *win_state;
};
extern const struct m_sub_options gl_video_conf;
@@ -229,8 +232,10 @@ static void update_opts(void *p)
read_opts(vo);
// "Legacy" update of video position related options.
- if (vo->driver->control)
+ if (vo->driver->control) {
vo->driver->control(vo, VOCTRL_SET_PANSCAN, NULL);
+ vo->driver->control(vo, VOCTRL_VO_WIN_STATE_UPDATE, NULL);
+ }
}
if (vo->gl_opts_cache && m_config_cache_update(vo->gl_opts_cache)) {
@@ -1069,6 +1074,7 @@ static void *vo_thread(void *ptr)
vo->driver->uninit(vo);
done:
TA_FREEP(&in->dr_helper);
+ assert(!in->win_state);
return NULL;
}
@@ -1352,6 +1358,32 @@ struct mp_image *vo_get_image(struct vo *vo, int imgfmt, int w, int h,
return NULL;
}
+void vo_set_internal_win_state(struct vo *vo, struct vo_win_state *st)
+{
+ struct vo_internal *in = vo->in;
+ pthread_mutex_lock(&in->lock);
+ assert(!!in->win_state != !!st); // can either set or unset it
+ in->win_state = st;
+ pthread_mutex_unlock(&in->lock);
+}
+
+int vo_win_state_fetch_ext_wrap(struct vo *vo, int state,
+ union m_option_value *val)
+{
+ struct vo_internal *in = vo->in;
+ int res = -1;
+ pthread_mutex_lock(&in->lock);
+
+ if (in->win_state) {
+ res = vo_win_state_fetch_ext(in->win_state, state, val);
+ } else {
+ *val = (union m_option_value){0};
+ }
+
+ pthread_mutex_unlock(&in->lock);
+ return res;
+}
+
static void destroy_frame(void *p)
{
struct vo_frame *frame = p;
diff --git a/video/out/vo.h b/video/out/vo.h
index 08ca1219a1..a68912ec84 100644
--- a/video/out/vo.h
+++ b/video/out/vo.h
@@ -37,7 +37,8 @@ enum {
VO_EVENT_RESIZE = 1 << 1,
// The ICC profile needs to be reloaded
VO_EVENT_ICC_PROFILE_CHANGED = 1 << 2,
- // Some other window state changed (position, window state, fps)
+ // When display fps or window position changes.
+ // Legacy: some other window state changed (fullscreen etc.)
VO_EVENT_WIN_STATE = 1 << 3,
// The ambient light conditions changed and need to be reloaded
VO_EVENT_AMBIENT_LIGHTING_CHANGED = 1 << 4,
@@ -48,10 +49,13 @@ enum {
// Special thing for encode mode (vo_driver.initially_blocked).
// Part of VO_EVENTS_USER to make vo_is_ready_for_frame() work properly.
VO_EVENT_INITIAL_UNBLOCK = 1 << 7,
+ // Triggered by vo_win_state. Other code does not mess with this.
+ VO_EVENT_WIN_STATE2 = 1 << 8,
// Set of events the player core may be interested in.
VO_EVENTS_USER = VO_EVENT_RESIZE | VO_EVENT_WIN_STATE |
- VO_EVENT_FULLSCREEN_STATE | VO_EVENT_INITIAL_UNBLOCK,
+ VO_EVENT_FULLSCREEN_STATE | VO_EVENT_INITIAL_UNBLOCK |
+ VO_EVENT_WIN_STATE2,
};
enum mp_voctrl {
@@ -80,12 +84,16 @@ enum mp_voctrl {
VOCTRL_UNINIT,
VOCTRL_RECONFIG,
+ // Legacy, do not use.
VOCTRL_FULLSCREEN,
+ VOCTRL_GET_FULLSCREEN,
VOCTRL_ONTOP,
VOCTRL_BORDER,
VOCTRL_ALL_WORKSPACES,
+ VOCTRL_GET_WIN_STATE, // int* (VO_WIN_STATE_* flags)
- VOCTRL_GET_FULLSCREEN,
+ // If you use vo_win_state, call vo_win_state_update().
+ VOCTRL_VO_WIN_STATE_UPDATE,
VOCTRL_UPDATE_WINDOW_TITLE, // char*
VOCTRL_UPDATE_PLAYBACK_STATE, // struct voctrl_playback_state*
@@ -102,8 +110,6 @@ enum mp_voctrl {
VOCTRL_GET_UNFS_WINDOW_SIZE, // int[2] (w/h)
VOCTRL_SET_UNFS_WINDOW_SIZE, // int[2] (w/h)
- VOCTRL_GET_WIN_STATE, // int* (VO_WIN_STATE_* flags)
-
// char *** (NULL terminated array compatible with CONF_TYPE_STRING_LIST)
// names for displays the window is on
VOCTRL_GET_DISPLAY_NAMES,
@@ -520,6 +526,12 @@ struct mp_image *vo_get_image(struct vo *vo, int imgfmt, int w, int h,
void vo_wakeup(struct vo *vo);
void vo_wait_default(struct vo *vo, int64_t until_time);
+// Internal for VO common code. VO backends do not call this.
+struct vo_win_state;
+void vo_set_internal_win_state(struct vo *vo, struct vo_win_state *st);
+int vo_win_state_fetch_ext_wrap(struct vo *vo, int state,
+ union m_option_value *val);
+
struct mp_keymap {
int from;
int to;
diff --git a/video/out/win_state.c b/video/out/win_state.c
index 96857160cf..473cc38d62 100644
--- a/video/out/win_state.c
+++ b/video/out/win_state.c
@@ -15,6 +15,11 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <pthread.h>
+
+#include "common/common.h"
+#include "options/m_config.h"
+#include "options/m_option.h"
#include "win_state.h"
#include "vo.h"
@@ -140,3 +145,178 @@ void vo_apply_window_geometry(struct vo *vo, const struct vo_win_geometry *geo)
vo->dheight = geo->win.y1 - geo->win.y0;
vo->monitor_par = geo->monitor_par;
}
+
+struct vo_win_state {
+ pthread_mutex_t lock;
+
+ struct vo *vo;
+
+ struct m_config_cache *opts_cache;
+ struct mp_vo_opts *opts;
+
+ struct m_option types[VO_WIN_STATE_COUNT];
+ void *opt_map[VO_WIN_STATE_COUNT];
+ uint64_t external_changed; // VO_WIN_STATE_* bit field
+ // If external_changed bit set, this is the external "fixed" value.
+ // Otherwise, this is the current/previous value.
+ union m_option_value fixed[VO_WIN_STATE_COUNT];
+
+ // These are not options.
+ int minimize, maximize;
+};
+
+static void win_state_destroy(void *p)
+{
+ struct vo_win_state *st = p;
+
+ vo_set_internal_win_state(st->vo, NULL);
+
+ pthread_mutex_destroy(&st->lock);
+}
+
+struct vo_win_state *vo_win_state_create(struct vo *vo)
+{
+ struct vo_win_state *st = talloc_zero(NULL, struct vo_win_state);
+ talloc_set_destructor(st, win_state_destroy);
+
+ pthread_mutex_init(&st->lock, NULL);
+
+ st->vo = vo;
+
+ st->opts_cache = m_config_cache_alloc(st, vo->global, &vo_sub_opts);
+ st->opts = st->opts_cache->opts;
+
+ st->opt_map[VO_WIN_STATE_FULLSCREEN] = &st->opts->fullscreen;
+ st->opt_map[VO_WIN_STATE_MINIMIZE] = &st->minimize;
+ st->opt_map[VO_WIN_STATE_MAXIMIZE] = &st->maximize;
+ st->opt_map[VO_WIN_STATE_ON_TOP] = &st->opts->ontop;
+ st->opt_map[VO_WIN_STATE_BORDER] = &st->opts->border;
+ st->opt_map[VO_WIN_STATE_ALL_WS] = &st->opts->all_workspaces;
+
+ for (int n = 0; n < MP_ARRAY_SIZE(st->opt_map); n++) {
+ // Currently all the same.
+ st->types[n] = (struct m_option){
+ .type = &m_option_type_flag,
+ };
+
+ // Copy initial value.
+ m_option_copy(&st->types[n], &st->fixed[n], st->opt_map[n]);
+ }
+
+ vo_set_internal_win_state(vo, st);
+ return st;
+}
+
+char *vo_win_state_opt(enum vo_win_states state)
+{
+ switch (state) {
+ case VO_WIN_STATE_FULLSCREEN: return "fullscreen";
+ case VO_WIN_STATE_ON_TOP: return "ontop";
+ case VO_WIN_STATE_BORDER: return "border";
+ case VO_WIN_STATE_ALL_WS: return "on-all-workspaces";
+ default: return NULL; // not managed as option
+ }
+}
+
+struct mp_vo_opts *vo_win_state_opts(struct vo_win_state *st)
+{
+ return st->opts;
+}
+
+// (The generic option code does not have this because it's too complex to
+// support for _all_ option types.)
+static bool opt_equals(struct m_option *t, void *v1, void *v2)
+{
+ if (t->type == &m_option_type_flag) {
+ return *(int *)v1 == *(int *)v2;
+ } else {
+ assert(0); // well, add it
+ }
+}
+
+uint64_t vo_win_state_update(struct vo_win_state *st)
+{
+ uint64_t changed = 0;
+
+ pthread_mutex_lock(&st->lock);
+
+ if (m_config_cache_update(st->opts_cache)) {
+ // Ignore changes to any "fixed" fields, but return other changed fields.
+ for (int n = 0; n < MP_ARRAY_SIZE(st->opt_map); n++) {
+ if (st->external_changed & (1ull << n))
+ m_option_copy(&st->types[n], st->opt_map[n], &st->fixed[n]);
+
+ if (!opt_equals(&st->types[n], st->opt_map[n], &st->fixed[n])) {
+ changed |= (1ull << n);
+
+ m_option_copy(&st->types[n], &st->fixed[n], st->opt_map[n]);
+ }
+ }
+ }
+
+ pthread_mutex_unlock(&st->lock);
+
+ return changed;
+}
+
+bool vo_win_state_get_bool(struct vo_win_state *st, enum vo_win_states state)
+{
+ assert(st->types[state].type == &m_option_type_flag);
+ return *(int *)st->opt_map[state];
+}
+
+void vo_win_state_report_bool(struct vo_win_state *st, enum vo_win_states state,
+ bool val)
+{
+ assert(st->types[state].type == &m_option_type_flag);
+ *(int *)st->opt_map[state] = val;
+ vo_win_state_report_external_changed(st, st->opt_map[state]);
+}
+
+void vo_win_state_report_external_changed(struct vo_win_state *st, void *field)
+{
+ int state = -1;
+ for (int n = 0; n < MP_ARRAY_SIZE(st->opt_map); n++) {
+ if (st->opt_map[n] == field) {
+ state = n;
+ break;
+ }
+ }
+
+ assert(state >= 0); // user passed non-managed field, or uses wrong opt. struct
+
+ // "Fix" the option to avoid that concurrent or recursive option updates
+ // clobber it (urgh).
+ pthread_mutex_lock(&st->lock);
+ st->external_changed |= 1ull << state;
+ m_option_copy(&st->types[state], &st->fixed[state], field);
+ pthread_mutex_unlock(&st->lock);
+
+ // Causes some magic code to call vo_win_state_fetch_ext() to reset the
+ // fixed option.
+ vo_event(st->vo, VO_EVENT_WIN_STATE2);
+}
+
+int vo_win_state_fetch_ext(struct vo_win_state *st, int state,
+ union m_option_value *val)
+{
+ pthread_mutex_lock(&st->lock);
+
+ if (state < 0) {
+ for (int n = 0; n < MP_ARRAY_SIZE(st->opt_map); n++) {
+ uint64_t mask = 1ull << n;
+ if (st->external_changed & mask) {
+ st->external_changed &= ~mask;
+ state = n;
+ break;
+ }
+ }
+ }
+
+ if (state >= 0)
+ m_option_copy(&st->types[state], val, st->opt_map[state]);
+
+ pthread_mutex_unlock(&st->lock);
+
+ return state;
+}
diff --git a/video/out/win_state.h b/video/out/win_state.h
index d495377bac..a5de5691fb 100644
--- a/video/out/win_state.h
+++ b/video/out/win_state.h
@@ -29,4 +29,61 @@ void vo_calc_window_geometry2(struct vo *vo, const struct mp_rect *screen,
double dpi_scale, struct vo_win_geometry *out_geo);
void vo_apply_window_geometry(struct vo *vo, const struct vo_win_geometry *geo);
+// Currently manages some user options.
+struct vo_win_state;
+
+enum vo_win_states {
+ VO_WIN_STATE_FULLSCREEN, // bool
+ VO_WIN_STATE_MINIMIZE, // bool
+ VO_WIN_STATE_MAXIMIZE, // bool
+ VO_WIN_STATE_ON_TOP, // bool
+ VO_WIN_STATE_BORDER, // bool
+ VO_WIN_STATE_ALL_WS, // bool
+
+ VO_WIN_STATE_COUNT
+};
+
+// Destroy with talloc_free().
+// Note: this must be strictly destroyed before vo.
+// This hooks itself into vo (in a thread-safe way), and you can have only one
+// per vo.
+struct vo_win_state *vo_win_state_create(struct vo *vo);
+
+// Note: it's _not_ OK to use vo->opts instead (e.g. vo->opts->fullscreen
+// instead of this opt struct. This is because vo->opts is managed for the
+// VO thread, so this breaks with backends that do windowing on a foreign
+// thread. You may also use vo_get_win_opts().
+struct mp_vo_opts *vo_win_state_opts(struct vo_win_state *st);
+
+// Update state in reaction to other events. Normally, you want to call this
+// when receiving VOCTRL_VO_STATE_UPDATE.
+// This returns a bit-field of externally changed event, using vo_win_states as
+// bit position. E.g. if fullscreen and on-top changes, this would return
+// (1 << VO_WIN_STATE_FULLSCREEN) | (1 << VO_WIN_STATE_ON_TOP).
+uint64_t vo_win_state_update(struct vo_win_state *st);
+
+// Query the current user-desired state (basically, return the option value).
+// This is equivalent to using vo_win_state_opts()->[field mapping to state].
+bool vo_win_state_get_bool(struct vo_win_state *st, enum vo_win_states state);
+
+// Update the current state, usually in reaction to external events.
+// This is equivalent to setting vo_win_state_opts()->[field mapping to state]
+// to the new value, and calling vo_win_state_mark_external_changed().
+void vo_win_state_report_bool(struct vo_win_state *st, enum vo_win_states state,
+ bool val);
+
+// Notify that the current state was updated, usually in reaction to external
+// events. "field" must be a pointer to a field in vo_win_state_opts() (that
+// is an option, and a direct member of struct mp_subtitle_opts).
+void vo_win_state_report_external_changed(struct vo_win_state *st, void *field);
+
+// Internal: get and reset next changed state (option-managed fields only).
+// Returns state value if it was externally changed, or -1 if not.
+// If state<0, then get next changed state, otherwise use fixed state.
+int vo_win_state_fetch_ext(struct vo_win_state *st, int state,
+ union m_option_value *val);
+
+// Internal, a hack.
+char *vo_win_state_opt(enum vo_win_states state);
+
#endif