summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--player/playloop.c23
-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
5 files changed, 308 insertions, 8 deletions
diff --git a/player/playloop.c b/player/playloop.c
index 5c83615a86..585cad93c9 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -45,6 +45,7 @@
#include "sub/dec_sub.h"
#include "sub/osd.h"
#include "video/out/vo.h"
+#include "video/out/win_state.h"
#include "core.h"
#include "client.h"
@@ -832,10 +833,12 @@ static void handle_cursor_autohide(struct MPContext *mpctx)
static void handle_vo_events(struct MPContext *mpctx)
{
struct vo *vo = mpctx->video_out;
- int events = vo ? vo_query_and_reset_events(vo, VO_EVENTS_USER) : 0;
+ if (!vo)
+ return;
+ int events = vo_query_and_reset_events(vo, VO_EVENTS_USER);
if (events & VO_EVENT_RESIZE)
mp_notify(mpctx, MP_EVENT_WIN_RESIZE, NULL);
- if (events & VO_EVENT_WIN_STATE)
+ if (events & (VO_EVENT_WIN_STATE | VO_EVENT_WIN_STATE2))
mp_notify(mpctx, MP_EVENT_WIN_STATE, NULL);
if (events & VO_EVENT_FULLSCREEN_STATE) {
// The only purpose of this is to update the fullscreen flag on the
@@ -848,6 +851,22 @@ static void handle_vo_events(struct MPContext *mpctx)
m_config_get_co(mpctx->mconfig, bstr0("fullscreen")), &fs, 0);
}
}
+ if (events & VO_EVENT_WIN_STATE2) {
+ // The only purpose of this is to update the option values on the
+ // playloop side if it changes "from outside" on the VO. This will be
+ // unnecessary once the core can deal with asynchronous option changes.
+ while (1) {
+ union m_option_value val;
+ int st = vo_win_state_fetch_ext_wrap(vo, -1, &val);
+ if (st < 0)
+ break;
+ char *optname = vo_win_state_opt(st);
+ if (optname) {
+ m_config_set_option_raw(mpctx->mconfig,
+ m_config_get_co(mpctx->mconfig, bstr0(optname)), &val, 0);
+ }
+ }
+ }
}
static void handle_sstep(struct MPContext *mpctx)
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