summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--video/out/cocoa_cb_common.swift59
-rw-r--r--video/out/d3d11/context.c75
-rw-r--r--video/out/d3d11/context.h4
-rw-r--r--video/out/drm_common.c9
-rw-r--r--video/out/drm_common.h11
-rw-r--r--video/out/filter_kernels.c4
-rw-r--r--video/out/gpu/context.c11
-rw-r--r--video/out/gpu/context.h2
-rw-r--r--video/out/gpu/d3d11_helpers.c129
-rw-r--r--video/out/gpu/d3d11_helpers.h19
-rw-r--r--video/out/gpu/error_diffusion.c3
-rw-r--r--video/out/gpu/hwdec.c4
-rw-r--r--video/out/gpu/hwdec.h8
-rw-r--r--video/out/gpu/lcms.c62
-rw-r--r--video/out/gpu/lcms.h6
-rw-r--r--video/out/gpu/libmpv_gpu.c2
-rw-r--r--video/out/gpu/osd.c4
-rw-r--r--video/out/gpu/osd.h2
-rw-r--r--video/out/gpu/ra.h4
-rw-r--r--video/out/gpu/spirv.c1
-rw-r--r--video/out/gpu/user_shaders.c4
-rw-r--r--video/out/gpu/user_shaders.h2
-rw-r--r--video/out/gpu/utils.c6
-rw-r--r--video/out/gpu/utils.h2
-rw-r--r--video/out/gpu/video.c358
-rw-r--r--video/out/gpu/video.h17
-rw-r--r--video/out/gpu/video_shaders.c128
-rw-r--r--video/out/gpu/video_shaders.h9
-rw-r--r--video/out/gpu_next/context.c25
-rw-r--r--video/out/gpu_next/context.h4
-rw-r--r--video/out/hwdec/dmabuf_interop.h2
-rw-r--r--video/out/hwdec/dmabuf_interop_gl.c176
-rw-r--r--video/out/hwdec/dmabuf_interop_pl.c2
-rw-r--r--video/out/hwdec/hwdec_aimagereader.c12
-rw-r--r--video/out/hwdec/hwdec_cuda.c2
-rw-r--r--video/out/hwdec/hwdec_drmprime.c27
-rw-r--r--video/out/hwdec/hwdec_vaapi.c10
-rw-r--r--video/out/hwdec/hwdec_vt.c2
-rw-r--r--video/out/mac/common.swift228
-rw-r--r--video/out/mac/gl_layer.swift32
-rw-r--r--video/out/mac/metal_layer.swift12
-rw-r--r--video/out/mac/title_bar.swift16
-rw-r--r--video/out/mac/view.swift152
-rw-r--r--video/out/mac/window.swift83
-rw-r--r--video/out/mac_common.swift75
-rw-r--r--video/out/opengl/common.c2
-rw-r--r--video/out/opengl/context_drm_egl.c4
-rw-r--r--video/out/opengl/context_glx.c2
-rw-r--r--video/out/opengl/context_rpi.c327
-rw-r--r--video/out/opengl/context_wayland.c4
-rw-r--r--video/out/opengl/context_win.c22
-rw-r--r--video/out/opengl/egl_helpers.c10
-rw-r--r--video/out/opengl/formats.c2
-rw-r--r--video/out/opengl/hwdec_rpi.c384
-rw-r--r--video/out/placebo/ra_pl.c2
-rw-r--r--video/out/placebo/utils.c206
-rw-r--r--video/out/placebo/utils.h13
-rw-r--r--video/out/vo.c127
-rw-r--r--video/out/vo.h28
-rw-r--r--video/out/vo_direct3d.c13
-rw-r--r--video/out/vo_dmabuf_wayland.c32
-rw-r--r--video/out/vo_drm.c85
-rw-r--r--video/out/vo_gpu.c30
-rw-r--r--video/out/vo_gpu_next.c511
-rw-r--r--video/out/vo_image.c2
-rw-r--r--video/out/vo_kitty.c5
-rw-r--r--video/out/vo_lavc.c6
-rw-r--r--video/out/vo_libmpv.c14
-rw-r--r--video/out/vo_rpi.c938
-rw-r--r--video/out/vo_sdl.c7
-rw-r--r--video/out/vo_tct.c154
-rw-r--r--video/out/vo_vaapi.c2
-rw-r--r--video/out/vo_vdpau.c32
-rw-r--r--video/out/vo_wlshm.c16
-rw-r--r--video/out/vo_x11.c4
-rw-r--r--video/out/vo_xv.c14
-rw-r--r--video/out/vulkan/common.h1
-rw-r--r--video/out/vulkan/context.c49
-rw-r--r--video/out/vulkan/context_display.c44
-rw-r--r--video/out/vulkan/context_mac.m22
-rw-r--r--video/out/vulkan/context_wayland.c2
-rw-r--r--video/out/vulkan/context_win.c21
-rw-r--r--video/out/w32_common.c457
-rw-r--r--video/out/w32_common.h1
-rw-r--r--video/out/wayland_common.c1008
-rw-r--r--video/out/wayland_common.h31
-rw-r--r--video/out/win32/droptarget.c5
-rw-r--r--video/out/win32/menu.c231
-rw-r--r--video/out/win32/menu.h (renamed from osdep/macosx_menubar.h)24
-rw-r--r--video/out/x11_common.c203
-rw-r--r--video/out/x11_common.h8
91 files changed, 3171 insertions, 3739 deletions
diff --git a/video/out/cocoa_cb_common.swift b/video/out/cocoa_cb_common.swift
index 9c0054a..9f32ed6 100644
--- a/video/out/cocoa_cb_common.swift
+++ b/video/out/cocoa_cb_common.swift
@@ -17,11 +17,11 @@
import Cocoa
-class CocoaCB: Common {
+class CocoaCB: Common, EventSubscriber {
var libmpv: LibmpvHelper
var layer: GLLayer?
- @objc var isShuttingDown: Bool = false
+ var isShuttingDown: Bool = false
enum State {
case uninitialized
@@ -30,22 +30,24 @@ class CocoaCB: Common {
}
var backendState: State = .uninitialized
-
- @objc init(_ mpvHandle: OpaquePointer) {
- let newlog = mp_log_new(UnsafeMutablePointer<MPContext>(mpvHandle), mp_client_get_log(mpvHandle), "cocoacb")
- libmpv = LibmpvHelper(mpvHandle, newlog)
- super.init(newlog)
+ init(_ mpv: OpaquePointer) {
+ let log = LogHelper(mp_log_new(UnsafeMutablePointer(mpv), mp_client_get_log(mpv), "cocoacb"))
+ let option = OptionHelper(UnsafeMutablePointer(mpv), mp_client_get_global(mpv))
+ libmpv = LibmpvHelper(mpv, log)
+ super.init(option, log)
layer = GLLayer(cocoaCB: self)
+ AppHub.shared.event?.subscribe(self, event: .init(name: "MPV_EVENT_SHUTDOWN"))
}
func preinit(_ vo: UnsafeMutablePointer<vo>) {
- mpv = MPVHelper(vo, log)
+ self.vo = vo
+ input = InputHelper(vo.pointee.input_ctx, option)
if backendState == .uninitialized {
backendState = .needsInit
guard let layer = self.layer else {
- log.sendError("Something went wrong, no GLLayer was initialized")
+ log.error("Something went wrong, no GLLayer was initialized")
exit(1)
}
@@ -57,17 +59,19 @@ class CocoaCB: Common {
func uninit() {
window?.orderOut(nil)
window?.close()
- mpv = nil
}
func reconfig(_ vo: UnsafeMutablePointer<vo>) {
- mpv?.vo = vo
+ self.vo = vo
if backendState == .needsInit {
DispatchQueue.main.sync { self.initBackend(vo) }
- } else {
+ } else if option.vo.auto_window_resize {
DispatchQueue.main.async {
self.updateWindowSize(vo)
self.layer?.update(force: true)
+ if self.option.vo.focus_on == 2 {
+ NSApp.activate(ignoringOtherApps: true)
+ }
}
}
}
@@ -85,15 +89,12 @@ class CocoaCB: Common {
func updateWindowSize(_ vo: UnsafeMutablePointer<vo>) {
guard let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main else
{
- log.sendWarning("Couldn't update Window size, no Screen available")
+ log.warning("Couldn't update Window size, no Screen available")
return
}
let wr = getWindowGeometry(forScreen: targetScreen, videoOut: vo)
- if !(window?.isVisible ?? false) &&
- !(window?.isMiniaturized ?? false) &&
- !NSApp.isHidden
- {
+ if !(window?.isVisible ?? false) && !(window?.isMiniaturized ?? false) && !NSApp.isHidden {
window?.makeKeyAndOrderFront(nil)
}
layer?.atomicDrawingStart()
@@ -116,7 +117,7 @@ class CocoaCB: Common {
override func updateICCProfile() {
guard let colorSpace = window?.screen?.colorSpace else {
- log.sendWarning("Couldn't update ICC Profile, no color space available")
+ log.warning("Couldn't update ICC Profile, no color space available")
return
}
@@ -169,7 +170,7 @@ class CocoaCB: Common {
let ccb = unsafeBitCast(ctx, to: CocoaCB.self)
guard let vo = v, let events = e else {
- ccb.log.sendWarning("Unexpected nil value in Control Callback")
+ ccb.log.warning("Unexpected nil value in Control Callback")
return VO_FALSE
}
@@ -198,9 +199,9 @@ class CocoaCB: Common {
return super.control(vo, events: events, request: request, data: data)
}
- func shutdown(_ destroy: Bool = false) {
+ func shutdown() {
isShuttingDown = window?.isAnimating ?? false ||
- window?.isInFullscreen ?? false && mpv?.opts.native_fs ?? true
+ window?.isInFullscreen ?? false && option.vo.native_fs
if window?.isInFullscreen ?? false && !(window?.isAnimating ?? false) {
window?.close()
}
@@ -209,22 +210,18 @@ class CocoaCB: Common {
uninit()
uninitCommon()
- libmpv.deinitRender()
- libmpv.deinitMPV(destroy)
+ layer?.lockCglContext()
+ libmpv.uninit()
+ layer?.unlockCglContext()
}
func checkShutdown() {
if isShuttingDown {
- shutdown(true)
+ shutdown()
}
}
- @objc func processEvent(_ event: UnsafePointer<mpv_event>) {
- switch event.pointee.event_id {
- case MPV_EVENT_SHUTDOWN:
- shutdown()
- default:
- break
- }
+ func handle(event: EventHelper.Event) {
+ if event.name == String(describing: MPV_EVENT_SHUTDOWN) { shutdown() }
}
}
diff --git a/video/out/d3d11/context.c b/video/out/d3d11/context.c
index 05f04fd..c563b5f 100644
--- a/video/out/d3d11/context.c
+++ b/video/out/d3d11/context.c
@@ -27,10 +27,6 @@
#include "context.h"
#include "ra_d3d11.h"
-static int d3d11_validate_adapter(struct mp_log *log,
- const struct m_option *opt,
- struct bstr name, const char **value);
-
struct d3d11_opts {
int feature_level;
int warp;
@@ -62,7 +58,7 @@ const struct m_sub_options d3d11_conf = {
{"d3d11-flip", OPT_BOOL(flip)},
{"d3d11-sync-interval", OPT_INT(sync_interval), M_RANGE(0, 4)},
{"d3d11-adapter", OPT_STRING_VALIDATE(adapter_name,
- d3d11_validate_adapter)},
+ mp_dxgi_validate_adapter)},
{"d3d11-output-format", OPT_CHOICE(output_format,
{"auto", DXGI_FORMAT_UNKNOWN},
{"rgba8", DXGI_FORMAT_R8G8B8A8_UNORM},
@@ -100,7 +96,7 @@ struct priv {
struct ra_tex *backbuffer;
ID3D11Device *device;
IDXGISwapChain *swapchain;
- struct mp_colorspace swapchain_csp;
+ struct pl_color_space swapchain_csp;
int64_t perf_freq;
unsigned sync_refresh_count;
@@ -109,37 +105,6 @@ struct priv {
int64_t last_submit_qpc;
};
-static int d3d11_validate_adapter(struct mp_log *log,
- const struct m_option *opt,
- struct bstr name, const char **value)
-{
- struct bstr param = bstr0(*value);
- bool help = bstr_equals0(param, "help");
- bool adapter_matched = false;
- struct bstr listing = { 0 };
-
- if (bstr_equals0(param, "")) {
- return 0;
- }
-
- adapter_matched = mp_d3d11_list_or_verify_adapters(log,
- help ? bstr0(NULL) : param,
- help ? &listing : NULL);
-
- if (help) {
- mp_info(log, "Available D3D11 adapters:\n%.*s",
- BSTR_P(listing));
- talloc_free(listing.start);
- return M_OPT_EXIT;
- }
-
- if (!adapter_matched) {
- mp_err(log, "No adapter matching '%.*s'!\n", BSTR_P(param));
- }
-
- return adapter_matched ? 0 : M_OPT_INVALID;
-}
-
static struct ra_tex *get_backbuffer(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
@@ -189,6 +154,11 @@ static bool d3d11_reconfig(struct ra_ctx *ctx)
static int d3d11_color_depth(struct ra_swapchain *sw)
{
struct priv *p = sw->priv;
+
+ DXGI_OUTPUT_DESC1 desc1;
+ if (mp_get_dxgi_output_desc(p->swapchain, &desc1))
+ return desc1.BitsPerColor;
+
DXGI_SWAP_CHAIN_DESC desc;
HRESULT hr = IDXGISwapChain_GetDesc(p->swapchain, &desc);
@@ -514,6 +484,9 @@ static bool d3d11_init(struct ra_ctx *ctx)
if (!vo_w32_init(ctx->vo))
goto error;
+ if (ctx->opts.want_alpha)
+ vo_w32_set_transparency(ctx->vo, ctx->opts.want_alpha);
+
UINT usage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
if (ID3D11Device_GetFeatureLevel(p->device) >= D3D_FEATURE_LEVEL_11_0 &&
p->opts->output_format != DXGI_FORMAT_B8G8R8A8_UNORM)
@@ -544,6 +517,11 @@ error:
return false;
}
+static void d3d11_update_render_opts(struct ra_ctx *ctx)
+{
+ vo_w32_set_transparency(ctx->vo, ctx->opts.want_alpha);
+}
+
IDXGISwapChain *ra_d3d11_ctx_get_swapchain(struct ra_ctx *ra)
{
if (ra->swapchain->fns != &d3d11_swapchain)
@@ -556,11 +534,22 @@ IDXGISwapChain *ra_d3d11_ctx_get_swapchain(struct ra_ctx *ra)
return p->swapchain;
}
+bool ra_d3d11_ctx_prefer_8bit_output_format(struct ra_ctx *ra)
+{
+ if (ra->swapchain->fns != &d3d11_swapchain)
+ return false;
+
+ struct priv *p = ra->priv;
+
+ return p->opts->output_format == DXGI_FORMAT_R8G8B8A8_UNORM;
+}
+
const struct ra_ctx_fns ra_ctx_d3d11 = {
- .type = "d3d11",
- .name = "d3d11",
- .reconfig = d3d11_reconfig,
- .control = d3d11_control,
- .init = d3d11_init,
- .uninit = d3d11_uninit,
+ .type = "d3d11",
+ .name = "d3d11",
+ .reconfig = d3d11_reconfig,
+ .control = d3d11_control,
+ .update_render_opts = d3d11_update_render_opts,
+ .init = d3d11_init,
+ .uninit = d3d11_uninit,
};
diff --git a/video/out/d3d11/context.h b/video/out/d3d11/context.h
index 8a9ef4c..25488f2 100644
--- a/video/out/d3d11/context.h
+++ b/video/out/d3d11/context.h
@@ -7,3 +7,7 @@
// Get the underlying D3D11 swap chain from an RA context. The returned swap chain is
// refcounted and must be released by the caller.
IDXGISwapChain *ra_d3d11_ctx_get_swapchain(struct ra_ctx *ra);
+
+// Returns true if an 8-bit output format is explicitly requested for
+// d3d11-output-format for an RA context.
+bool ra_d3d11_ctx_prefer_8bit_output_format(struct ra_ctx *ra);
diff --git a/video/out/drm_common.c b/video/out/drm_common.c
index da45ca2..e47de7d 100644
--- a/video/out/drm_common.c
+++ b/video/out/drm_common.c
@@ -65,8 +65,7 @@ static int drm_connector_opt_help(struct mp_log *log, const struct m_option *opt
static int drm_mode_opt_help(struct mp_log *log, const struct m_option *opt,
struct bstr name);
-static int drm_validate_mode_opt(struct mp_log *log, const struct m_option *opt,
- struct bstr name, const char **value);
+static OPT_STRING_VALIDATE_FUNC(drm_validate_mode_opt);
static void drm_show_available_modes(struct mp_log *log, const drmModeConnector *connector);
@@ -96,7 +95,8 @@ const struct m_sub_options drm_conf = {
{"xrgb8888", DRM_OPTS_FORMAT_XRGB8888},
{"xrgb2101010", DRM_OPTS_FORMAT_XRGB2101010},
{"xbgr8888", DRM_OPTS_FORMAT_XBGR8888},
- {"xbgr2101010", DRM_OPTS_FORMAT_XBGR2101010})},
+ {"xbgr2101010", DRM_OPTS_FORMAT_XBGR2101010},
+ {"yuyv", DRM_OPTS_FORMAT_YUYV})},
{"drm-draw-surface-size", OPT_SIZE_BOX(draw_surface_size)},
{"drm-vrr-enabled", OPT_CHOICE(vrr_enabled,
{"no", 0}, {"yes", 1}, {"auto", -1})},
@@ -107,6 +107,7 @@ const struct m_sub_options drm_conf = {
.drm_atomic = 1,
.draw_plane = DRM_OPTS_PRIMARY_PLANE,
.drmprime_video_plane = DRM_OPTS_OVERLAY_PLANE,
+ .drm_format = DRM_OPTS_FORMAT_XRGB8888,
},
.size = sizeof(struct drm_opts),
};
@@ -387,7 +388,7 @@ bool vo_drm_acquire_crtc(struct vo_drm_state *drm)
drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_H", drm->mode.mode.vdisplay);
if (drmModeAtomicCommit(drm->fd, request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL)) {
- MP_ERR(drm, "Failed to commit ModeSetting atomic request: %s\n", strerror(errno));
+ MP_ERR(drm, "Failed to commit ModeSetting atomic request: %s\n", mp_strerror(errno));
goto err;
}
diff --git a/video/out/drm_common.h b/video/out/drm_common.h
index 581151f..9418b3f 100644
--- a/video/out/drm_common.h
+++ b/video/out/drm_common.h
@@ -23,10 +23,13 @@
#include <xf86drmMode.h>
#include "vo.h"
-#define DRM_OPTS_FORMAT_XRGB8888 0
-#define DRM_OPTS_FORMAT_XRGB2101010 1
-#define DRM_OPTS_FORMAT_XBGR8888 2
-#define DRM_OPTS_FORMAT_XBGR2101010 3
+enum {
+ DRM_OPTS_FORMAT_XRGB8888,
+ DRM_OPTS_FORMAT_XRGB2101010,
+ DRM_OPTS_FORMAT_XBGR8888,
+ DRM_OPTS_FORMAT_XBGR2101010,
+ DRM_OPTS_FORMAT_YUYV,
+};
struct framebuffer {
int fd;
diff --git a/video/out/filter_kernels.c b/video/out/filter_kernels.c
index 95d99ff..d5df985 100644
--- a/video/out/filter_kernels.c
+++ b/video/out/filter_kernels.c
@@ -5,10 +5,10 @@
* (https://github.com/glumpy/glumpy/blob/master/glumpy/library/build-spatial-filters.py)
*
* Also see:
- * - http://vector-agg.cvs.sourceforge.net/viewvc/vector-agg/agg-2.5/include/agg_image_filters.h
+ * - https://sourceforge.net/p/agg/svn/HEAD/tree/agg-2.4/include/agg_image_filters.h
* - Vapoursynth plugin fmtconv (WTFPL Licensed), which is based on
* dither plugin for avisynth from the same author:
- * https://github.com/vapoursynth/fmtconv/tree/master/src/fmtc
+ * https://gitlab.com/EleonoreMizo/fmtconv/-/tree/master/src/fmtc
* - Paul Heckbert's "zoom"
* - XBMC: ConvolutionKernels.cpp etc.
*
diff --git a/video/out/gpu/context.c b/video/out/gpu/context.c
index 5ce18af..88d4f42 100644
--- a/video/out/gpu/context.c
+++ b/video/out/gpu/context.c
@@ -41,7 +41,6 @@ extern const struct ra_ctx_fns ra_ctx_wayland_egl;
extern const struct ra_ctx_fns ra_ctx_wgl;
extern const struct ra_ctx_fns ra_ctx_angle;
extern const struct ra_ctx_fns ra_ctx_dxgl;
-extern const struct ra_ctx_fns ra_ctx_rpi;
extern const struct ra_ctx_fns ra_ctx_android;
/* Vulkan */
@@ -67,9 +66,6 @@ static const struct ra_ctx_fns *contexts[] = {
#if HAVE_EGL_ANDROID
&ra_ctx_android,
#endif
-#if HAVE_RPI
- &ra_ctx_rpi,
-#endif
#if HAVE_EGL_ANGLE_WIN32
&ra_ctx_angle,
#endif
@@ -133,8 +129,7 @@ static int ra_ctx_api_help(struct mp_log *log, const struct m_option *opt,
return M_OPT_EXIT;
}
-static int ra_ctx_validate_api(struct mp_log *log, const struct m_option *opt,
- struct bstr name, const char **value)
+static inline OPT_STRING_VALIDATE_FUNC(ra_ctx_validate_api)
{
struct bstr param = bstr0(*value);
if (bstr_equals0(param, "auto"))
@@ -158,8 +153,7 @@ static int ra_ctx_context_help(struct mp_log *log, const struct m_option *opt,
return M_OPT_EXIT;
}
-static int ra_ctx_validate_context(struct mp_log *log, const struct m_option *opt,
- struct bstr name, const char **value)
+static inline OPT_STRING_VALIDATE_FUNC(ra_ctx_validate_context)
{
struct bstr param = bstr0(*value);
if (bstr_equals0(param, "auto"))
@@ -208,6 +202,7 @@ struct ra_ctx *ra_ctx_create(struct vo *vo, struct ra_ctx_opts opts)
MP_VERBOSE(ctx, "Initializing GPU context '%s'\n", ctx->fns->name);
if (contexts[i]->init(ctx)) {
vo->probing = old_probing;
+ vo->context_name = ctx->fns->name;
return ctx;
}
diff --git a/video/out/gpu/context.h b/video/out/gpu/context.h
index 6788e6f..447f40b 100644
--- a/video/out/gpu/context.h
+++ b/video/out/gpu/context.h
@@ -72,7 +72,7 @@ struct ra_fbo {
// Host system's colorspace that it will be interpreting
// the frame buffer as.
- struct mp_colorspace color_space;
+ struct pl_color_space color_space;
};
struct ra_swapchain_fns {
diff --git a/video/out/gpu/d3d11_helpers.c b/video/out/gpu/d3d11_helpers.c
index 30d9eae..d45c038 100644
--- a/video/out/gpu/d3d11_helpers.c
+++ b/video/out/gpu/d3d11_helpers.c
@@ -228,9 +228,9 @@ static const char *d3d11_get_csp_name(DXGI_COLOR_SPACE_TYPE csp)
}
static bool d3d11_get_mp_csp(DXGI_COLOR_SPACE_TYPE csp,
- struct mp_colorspace *mp_csp)
+ struct pl_color_space *pl_color_system)
{
- if (!mp_csp)
+ if (!pl_color_system)
return false;
// Colorspaces utilizing gamma 2.2 (G22) are set to
@@ -243,27 +243,27 @@ static bool d3d11_get_mp_csp(DXGI_COLOR_SPACE_TYPE csp,
// regarding not doing conversion from BT.601 to BT.709.
switch (csp) {
case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709:
- *mp_csp = (struct mp_colorspace){
- .gamma = MP_CSP_TRC_AUTO,
- .primaries = MP_CSP_PRIM_AUTO,
+ *pl_color_system = (struct pl_color_space){
+ .transfer = PL_COLOR_TRC_UNKNOWN,
+ .primaries = PL_COLOR_PRIM_UNKNOWN,
};
break;
case DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709:
- *mp_csp = (struct mp_colorspace) {
- .gamma = MP_CSP_TRC_LINEAR,
- .primaries = MP_CSP_PRIM_AUTO,
+ *pl_color_system = (struct pl_color_space) {
+ .transfer = PL_COLOR_TRC_LINEAR,
+ .primaries = PL_COLOR_PRIM_UNKNOWN,
};
break;
case DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020:
- *mp_csp = (struct mp_colorspace) {
- .gamma = MP_CSP_TRC_PQ,
- .primaries = MP_CSP_PRIM_BT_2020,
+ *pl_color_system = (struct pl_color_space) {
+ .transfer = PL_COLOR_TRC_PQ,
+ .primaries = PL_COLOR_PRIM_BT_2020,
};
break;
case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020:
- *mp_csp = (struct mp_colorspace) {
- .gamma = MP_CSP_TRC_AUTO,
- .primaries = MP_CSP_PRIM_BT_2020,
+ *pl_color_system = (struct pl_color_space) {
+ .transfer = PL_COLOR_TRC_UNKNOWN,
+ .primaries = PL_COLOR_PRIM_BT_2020,
};
break;
default:
@@ -287,28 +287,8 @@ static bool query_output_format_and_colorspace(struct mp_log *log,
if (!out_fmt || !out_cspace)
return false;
- HRESULT hr = IDXGISwapChain_GetContainingOutput(swapchain, &output);
- if (FAILED(hr)) {
- mp_err(log, "Failed to get swap chain's containing output: %s!\n",
- mp_HRESULT_to_str(hr));
- goto done;
- }
-
- hr = IDXGIOutput_QueryInterface(output, &IID_IDXGIOutput6,
- (void**)&output6);
- if (FAILED(hr)) {
- // point where systems older than Windows 10 would fail,
- // thus utilizing error log level only with windows 10+
- mp_msg(log, IsWindows10OrGreater() ? MSGL_ERR : MSGL_V,
- "Failed to create a DXGI 1.6 output interface: %s\n",
- mp_HRESULT_to_str(hr));
- goto done;
- }
-
- hr = IDXGIOutput6_GetDesc1(output6, &desc);
- if (FAILED(hr)) {
- mp_err(log, "Failed to query swap chain's output information: %s\n",
- mp_HRESULT_to_str(hr));
+ if (!mp_get_dxgi_output_desc(swapchain, &desc)) {
+ mp_err(log, "Failed to query swap chain's output information\n");
goto done;
}
@@ -371,9 +351,9 @@ static int get_feature_levels(int max_fl, int min_fl,
return len;
}
-static IDXGIAdapter1 *get_d3d11_adapter(struct mp_log *log,
- struct bstr requested_adapter_name,
- struct bstr *listing)
+IDXGIAdapter1 *mp_get_dxgi_adapter(struct mp_log *log,
+ bstr requested_adapter_name,
+ bstr *listing)
{
HRESULT hr = S_OK;
IDXGIFactory1 *factory;
@@ -437,6 +417,37 @@ static IDXGIAdapter1 *get_d3d11_adapter(struct mp_log *log,
return picked_adapter;
}
+int mp_dxgi_validate_adapter(struct mp_log *log,
+ const struct m_option *opt,
+ struct bstr name, const char **value)
+{
+ struct bstr param = bstr0(*value);
+ bool help = bstr_equals0(param, "help");
+ bool adapter_matched = false;
+ struct bstr listing = { 0 };
+
+ if (bstr_equals0(param, "")) {
+ return 0;
+ }
+
+ adapter_matched = mp_dxgi_list_or_verify_adapters(log,
+ help ? bstr0(NULL) : param,
+ help ? &listing : NULL);
+
+ if (help) {
+ mp_info(log, "Available DXGI adapters:\n%.*s",
+ BSTR_P(listing));
+ talloc_free(listing.start);
+ return M_OPT_EXIT;
+ }
+
+ if (!adapter_matched) {
+ mp_err(log, "No adapter matching '%.*s'!\n", BSTR_P(param));
+ }
+
+ return adapter_matched ? 0 : M_OPT_INVALID;
+}
+
static HRESULT create_device(struct mp_log *log, IDXGIAdapter1 *adapter,
bool warp, bool debug, int max_fl, int min_fl,
ID3D11Device **dev)
@@ -455,9 +466,9 @@ static HRESULT create_device(struct mp_log *log, IDXGIAdapter1 *adapter,
NULL, flags, levels, levels_len, D3D11_SDK_VERSION, dev, NULL, NULL);
}
-bool mp_d3d11_list_or_verify_adapters(struct mp_log *log,
- bstr adapter_name,
- bstr *listing)
+bool mp_dxgi_list_or_verify_adapters(struct mp_log *log,
+ bstr adapter_name,
+ bstr *listing)
{
IDXGIAdapter1 *picked_adapter = NULL;
@@ -465,7 +476,7 @@ bool mp_d3d11_list_or_verify_adapters(struct mp_log *log,
return false;
}
- if ((picked_adapter = get_d3d11_adapter(log, adapter_name, listing))) {
+ if ((picked_adapter = mp_get_dxgi_adapter(log, adapter_name, listing))) {
SAFE_RELEASE(picked_adapter);
return true;
}
@@ -497,7 +508,7 @@ bool mp_d3d11_create_present_device(struct mp_log *log,
goto done;
}
- adapter = get_d3d11_adapter(log, bstr0(adapter_name), NULL);
+ adapter = mp_get_dxgi_adapter(log, bstr0(adapter_name), NULL);
if (adapter_name && !adapter) {
mp_warn(log, "Adapter matching '%s' was not found in the system! "
@@ -793,7 +804,7 @@ static bool configure_created_swapchain(struct mp_log *log,
IDXGISwapChain *swapchain,
DXGI_FORMAT requested_format,
DXGI_COLOR_SPACE_TYPE requested_csp,
- struct mp_colorspace *configured_csp)
+ struct pl_color_space *configured_csp)
{
DXGI_FORMAT probed_format = DXGI_FORMAT_UNKNOWN;
DXGI_FORMAT selected_format = DXGI_FORMAT_UNKNOWN;
@@ -801,7 +812,7 @@ static bool configure_created_swapchain(struct mp_log *log,
DXGI_COLOR_SPACE_TYPE selected_colorspace;
const char *format_name = NULL;
const char *csp_name = NULL;
- struct mp_colorspace mp_csp = { 0 };
+ struct pl_color_space pl_color_system = { 0 };
bool mp_csp_mapped = false;
query_output_format_and_colorspace(log, swapchain,
@@ -817,7 +828,7 @@ static bool configure_created_swapchain(struct mp_log *log,
requested_csp : probed_colorspace;
format_name = d3d11_get_format_name(selected_format);
csp_name = d3d11_get_csp_name(selected_colorspace);
- mp_csp_mapped = d3d11_get_mp_csp(selected_colorspace, &mp_csp);
+ mp_csp_mapped = d3d11_get_mp_csp(selected_colorspace, &pl_color_system);
mp_verbose(log, "Selected swapchain format %s (%d), attempting "
"to utilize it.\n",
@@ -848,7 +859,7 @@ static bool configure_created_swapchain(struct mp_log *log,
"mapping! Overriding to standard sRGB!\n",
csp_name, selected_colorspace);
selected_colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
- d3d11_get_mp_csp(selected_colorspace, &mp_csp);
+ d3d11_get_mp_csp(selected_colorspace, &pl_color_system);
}
mp_verbose(log, "Selected swapchain color space %s (%d), attempting to "
@@ -860,7 +871,7 @@ static bool configure_created_swapchain(struct mp_log *log,
}
if (configured_csp) {
- *configured_csp = mp_csp;
+ *configured_csp = pl_color_system;
}
return true;
@@ -964,3 +975,23 @@ done:
SAFE_RELEASE(dxgi_dev);
return success;
}
+
+bool mp_get_dxgi_output_desc(IDXGISwapChain *swapchain, DXGI_OUTPUT_DESC1 *desc)
+{
+ bool ret = false;
+ IDXGIOutput *output = NULL;
+ IDXGIOutput6 *output6 = NULL;
+
+ if (FAILED(IDXGISwapChain_GetContainingOutput(swapchain, &output)))
+ goto done;
+
+ if (FAILED(IDXGIOutput_QueryInterface(output, &IID_IDXGIOutput6, (void**)&output6)))
+ goto done;
+
+ ret = SUCCEEDED(IDXGIOutput6_GetDesc1(output6, desc));
+
+done:
+ SAFE_RELEASE(output);
+ SAFE_RELEASE(output6);
+ return ret;
+}
diff --git a/video/out/gpu/d3d11_helpers.h b/video/out/gpu/d3d11_helpers.h
index c115d33..6cc6818 100644
--- a/video/out/gpu/d3d11_helpers.h
+++ b/video/out/gpu/d3d11_helpers.h
@@ -22,6 +22,7 @@
#include <windows.h>
#include <d3d11.h>
#include <dxgi1_2.h>
+#include <dxgi1_6.h>
#include "video/mp_image.h"
@@ -65,9 +66,17 @@ struct d3d11_device_opts {
char *adapter_name;
};
-bool mp_d3d11_list_or_verify_adapters(struct mp_log *log,
- bstr adapter_name,
- bstr *listing);
+IDXGIAdapter1 *mp_get_dxgi_adapter(struct mp_log *log,
+ bstr requested_adapter_name,
+ bstr *listing);
+
+bool mp_get_dxgi_output_desc(IDXGISwapChain *swapchain, DXGI_OUTPUT_DESC1 *desc);
+
+OPT_STRING_VALIDATE_FUNC(mp_dxgi_validate_adapter);
+
+bool mp_dxgi_list_or_verify_adapters(struct mp_log *log,
+ bstr adapter_name,
+ bstr *listing);
bool mp_d3d11_create_present_device(struct mp_log *log,
struct d3d11_device_opts *opts,
@@ -80,10 +89,10 @@ struct d3d11_swapchain_opts {
DXGI_FORMAT format;
DXGI_COLOR_SPACE_TYPE color_space;
- // mp_colorspace mapping of the configured swapchain colorspace
+ // pl_color_space mapping of the configured swapchain colorspace
// shall be written into this memory location if configuration
// succeeds. Will be ignored if NULL.
- struct mp_colorspace *configured_csp;
+ struct pl_color_space *configured_csp;
// Use DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL if possible
bool flip;
diff --git a/video/out/gpu/error_diffusion.c b/video/out/gpu/error_diffusion.c
index c1ea542..72063c3 100644
--- a/video/out/gpu/error_diffusion.c
+++ b/video/out/gpu/error_diffusion.c
@@ -227,7 +227,8 @@ void pass_error_diffusion(struct gl_shader_cache *sc,
}
// Different kernels for error diffusion.
-// Patterns are from http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT
+// Patterns are from <https://web.archive.org/web/20181031005427/
+// http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT>
const struct error_diffusion_kernel mp_error_diffusion_kernels[] = {
{
.name = "simple",
diff --git a/video/out/gpu/hwdec.c b/video/out/gpu/hwdec.c
index c8098f3..c7d817c 100644
--- a/video/out/gpu/hwdec.c
+++ b/video/out/gpu/hwdec.c
@@ -34,7 +34,6 @@ extern const struct ra_hwdec_driver ra_hwdec_dxva2gldx;
extern const struct ra_hwdec_driver ra_hwdec_d3d11va;
extern const struct ra_hwdec_driver ra_hwdec_dxva2dxgi;
extern const struct ra_hwdec_driver ra_hwdec_cuda;
-extern const struct ra_hwdec_driver ra_hwdec_rpi_overlay;
extern const struct ra_hwdec_driver ra_hwdec_drmprime;
extern const struct ra_hwdec_driver ra_hwdec_drmprime_overlay;
extern const struct ra_hwdec_driver ra_hwdec_aimagereader;
@@ -70,9 +69,6 @@ const struct ra_hwdec_driver *const ra_hwdec_drivers[] = {
#if HAVE_VDPAU_GL_X11
&ra_hwdec_vdpau,
#endif
-#if HAVE_RPI_MMAL
- &ra_hwdec_rpi_overlay,
-#endif
#if HAVE_DRM
&ra_hwdec_drmprime,
&ra_hwdec_drmprime_overlay,
diff --git a/video/out/gpu/hwdec.h b/video/out/gpu/hwdec.h
index 7766073..f195606 100644
--- a/video/out/gpu/hwdec.h
+++ b/video/out/gpu/hwdec.h
@@ -18,12 +18,8 @@ struct ra_hwdec_ctx {
int num_hwdecs;
};
-int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, const char **value);
-
-int ra_hwdec_validate_drivers_only_opt(struct mp_log *log,
- const m_option_t *opt,
- struct bstr name, const char **value);
+OPT_STRING_VALIDATE_FUNC(ra_hwdec_validate_opt);
+OPT_STRING_VALIDATE_FUNC(ra_hwdec_validate_drivers_only_opt);
void ra_hwdec_ctx_init(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs,
const char *opt, bool load_all_by_default);
diff --git a/video/out/gpu/lcms.c b/video/out/gpu/lcms.c
index 7006a96..c197acf 100644
--- a/video/out/gpu/lcms.c
+++ b/video/out/gpu/lcms.c
@@ -46,8 +46,8 @@ struct gl_lcms {
char *current_profile;
bool using_memory_profile;
bool changed;
- enum mp_csp_prim current_prim;
- enum mp_csp_trc current_trc;
+ enum pl_color_primaries current_prim;
+ enum pl_color_transfer current_trc;
struct mp_log *log;
struct mpv_global *global;
@@ -162,8 +162,8 @@ static bool vid_profile_eq(struct AVBufferRef *a, struct AVBufferRef *b)
// Return whether the profile or config has changed since the last time it was
// retrieved. If it has changed, gl_lcms_get_lut3d() should be called.
-bool gl_lcms_has_changed(struct gl_lcms *p, enum mp_csp_prim prim,
- enum mp_csp_trc trc, struct AVBufferRef *vid_profile)
+bool gl_lcms_has_changed(struct gl_lcms *p, enum pl_color_primaries prim,
+ enum pl_color_transfer trc, struct AVBufferRef *vid_profile)
{
if (p->changed || p->current_prim != prim || p->current_trc != trc)
return true;
@@ -180,7 +180,7 @@ bool gl_lcms_has_profile(struct gl_lcms *p)
static cmsHPROFILE get_vid_profile(struct gl_lcms *p, cmsContext cms,
cmsHPROFILE disp_profile,
- enum mp_csp_prim prim, enum mp_csp_trc trc)
+ enum pl_color_primaries prim, enum pl_color_transfer trc)
{
if (p->opts->use_embedded && p->vid_profile) {
// Try using the embedded ICC profile
@@ -197,36 +197,41 @@ static cmsHPROFILE get_vid_profile(struct gl_lcms *p, cmsContext cms,
// The input profile for the transformation is dependent on the video
// primaries and transfer characteristics
- struct mp_csp_primaries csp = mp_get_csp_primaries(prim);
- cmsCIExyY wp_xyY = {csp.white.x, csp.white.y, 1.0};
+ const struct pl_raw_primaries *csp = pl_raw_primaries_get(prim);
+ cmsCIExyY wp_xyY = {csp->white.x, csp->white.y, 1.0};
cmsCIExyYTRIPLE prim_xyY = {
- .Red = {csp.red.x, csp.red.y, 1.0},
- .Green = {csp.green.x, csp.green.y, 1.0},
- .Blue = {csp.blue.x, csp.blue.y, 1.0},
+ .Red = {csp->red.x, csp->red.y, 1.0},
+ .Green = {csp->green.x, csp->green.y, 1.0},
+ .Blue = {csp->blue.x, csp->blue.y, 1.0},
};
cmsToneCurve *tonecurve[3] = {0};
switch (trc) {
- case MP_CSP_TRC_LINEAR: tonecurve[0] = cmsBuildGamma(cms, 1.0); break;
- case MP_CSP_TRC_GAMMA18: tonecurve[0] = cmsBuildGamma(cms, 1.8); break;
- case MP_CSP_TRC_GAMMA20: tonecurve[0] = cmsBuildGamma(cms, 2.0); break;
- case MP_CSP_TRC_GAMMA22: tonecurve[0] = cmsBuildGamma(cms, 2.2); break;
- case MP_CSP_TRC_GAMMA24: tonecurve[0] = cmsBuildGamma(cms, 2.4); break;
- case MP_CSP_TRC_GAMMA26: tonecurve[0] = cmsBuildGamma(cms, 2.6); break;
- case MP_CSP_TRC_GAMMA28: tonecurve[0] = cmsBuildGamma(cms, 2.8); break;
-
- case MP_CSP_TRC_SRGB:
+ case PL_COLOR_TRC_LINEAR: tonecurve[0] = cmsBuildGamma(cms, 1.0); break;
+ case PL_COLOR_TRC_GAMMA18: tonecurve[0] = cmsBuildGamma(cms, 1.8); break;
+ case PL_COLOR_TRC_GAMMA20: tonecurve[0] = cmsBuildGamma(cms, 2.0); break;
+ case PL_COLOR_TRC_GAMMA22: tonecurve[0] = cmsBuildGamma(cms, 2.2); break;
+ case PL_COLOR_TRC_GAMMA24: tonecurve[0] = cmsBuildGamma(cms, 2.4); break;
+ case PL_COLOR_TRC_GAMMA26: tonecurve[0] = cmsBuildGamma(cms, 2.6); break;
+ case PL_COLOR_TRC_GAMMA28: tonecurve[0] = cmsBuildGamma(cms, 2.8); break;
+
+ case PL_COLOR_TRC_ST428:
+ tonecurve[0] = cmsBuildParametricToneCurve(cms, 2,
+ (double[3]){2.6, pow(52.37/48.0, 1/2.6), 0.0});
+ break;
+
+ case PL_COLOR_TRC_SRGB:
// Values copied from Little-CMS
tonecurve[0] = cmsBuildParametricToneCurve(cms, 4,
(double[5]){2.40, 1/1.055, 0.055/1.055, 1/12.92, 0.04045});
break;
- case MP_CSP_TRC_PRO_PHOTO:
+ case PL_COLOR_TRC_PRO_PHOTO:
tonecurve[0] = cmsBuildParametricToneCurve(cms, 4,
(double[5]){1.8, 1.0, 0.0, 1/16.0, 0.03125});
break;
- case MP_CSP_TRC_BT_1886: {
+ case PL_COLOR_TRC_BT_1886: {
double src_black[3];
if (p->opts->contrast < 0) {
// User requested infinite contrast, return 2.4 profile
@@ -242,7 +247,7 @@ static cmsHPROFILE get_vid_profile(struct gl_lcms *p, cmsContext cms,
// function. Relative colorimetric is used since we want to
// approximate the BT.1886 to the target device's actual black
// point even in e.g. perceptual mode
- const int intent = MP_INTENT_RELATIVE_COLORIMETRIC;
+ const int intent = PL_INTENT_RELATIVE_COLORIMETRIC;
cmsCIEXYZ bp_XYZ;
if (!cmsDetectBlackPoint(&bp_XYZ, disp_profile, intent, 0))
return false;
@@ -300,7 +305,7 @@ static cmsHPROFILE get_vid_profile(struct gl_lcms *p, cmsContext cms,
}
bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
- enum mp_csp_prim prim, enum mp_csp_trc trc,
+ enum pl_color_primaries prim, enum pl_color_transfer trc,
struct AVBufferRef *vid_profile)
{
int s_r, s_g, s_b;
@@ -474,8 +479,8 @@ struct gl_lcms *gl_lcms_init(void *talloc_ctx, struct mp_log *log,
void gl_lcms_update_options(struct gl_lcms *p) { }
bool gl_lcms_set_memory_profile(struct gl_lcms *p, bstr profile) {return false;}
-bool gl_lcms_has_changed(struct gl_lcms *p, enum mp_csp_prim prim,
- enum mp_csp_trc trc, struct AVBufferRef *vid_profile)
+bool gl_lcms_has_changed(struct gl_lcms *p, enum pl_color_primaries prim,
+ enum pl_color_transfer trc, struct AVBufferRef *vid_profile)
{
return false;
}
@@ -486,7 +491,7 @@ bool gl_lcms_has_profile(struct gl_lcms *p)
}
bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
- enum mp_csp_prim prim, enum mp_csp_trc trc,
+ enum pl_color_primaries prim, enum pl_color_transfer trc,
struct AVBufferRef *vid_profile)
{
return false;
@@ -494,8 +499,7 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
#endif
-static int validate_3dlut_size_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, const char **value)
+static inline OPT_STRING_VALIDATE_FUNC(validate_3dlut_size_opt)
{
int p1, p2, p3;
return gl_parse_3dlut_size(*value, &p1, &p2, &p3) ? 0 : M_OPT_INVALID;
@@ -519,7 +523,7 @@ const struct m_sub_options mp_icc_conf = {
.size = sizeof(struct mp_icc_opts),
.defaults = &(const struct mp_icc_opts) {
.size_str = "auto",
- .intent = MP_INTENT_RELATIVE_COLORIMETRIC,
+ .intent = PL_INTENT_RELATIVE_COLORIMETRIC,
.use_embedded = true,
.cache = true,
},
diff --git a/video/out/gpu/lcms.h b/video/out/gpu/lcms.h
index 607353a..d0b0fe5 100644
--- a/video/out/gpu/lcms.h
+++ b/video/out/gpu/lcms.h
@@ -37,10 +37,10 @@ void gl_lcms_update_options(struct gl_lcms *p);
bool gl_lcms_set_memory_profile(struct gl_lcms *p, bstr profile);
bool gl_lcms_has_profile(struct gl_lcms *p);
bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **,
- enum mp_csp_prim prim, enum mp_csp_trc trc,
+ enum pl_color_primaries prim, enum pl_color_transfer trc,
struct AVBufferRef *vid_profile);
-bool gl_lcms_has_changed(struct gl_lcms *p, enum mp_csp_prim prim,
- enum mp_csp_trc trc, struct AVBufferRef *vid_profile);
+bool gl_lcms_has_changed(struct gl_lcms *p, enum pl_color_primaries prim,
+ enum pl_color_transfer trc, struct AVBufferRef *vid_profile);
static inline bool gl_parse_3dlut_size(const char *arg, int *p1, int *p2, int *p3)
{
diff --git a/video/out/gpu/libmpv_gpu.c b/video/out/gpu/libmpv_gpu.c
index aae1d18..542db7f 100644
--- a/video/out/gpu/libmpv_gpu.c
+++ b/video/out/gpu/libmpv_gpu.c
@@ -185,7 +185,7 @@ static int render(struct render_backend *ctx, mpv_render_param *params,
&(int){0});
struct ra_fbo target = {.tex = tex, .flip = flip};
- gl_video_render_frame(p->renderer, frame, target, RENDER_FRAME_DEF);
+ gl_video_render_frame(p->renderer, frame, &target, RENDER_FRAME_DEF);
p->context->fns->done_frame(p->context, frame->display_synced);
return 0;
diff --git a/video/out/gpu/osd.c b/video/out/gpu/osd.c
index 91505a9..7892904 100644
--- a/video/out/gpu/osd.c
+++ b/video/out/gpu/osd.c
@@ -286,7 +286,7 @@ static void get_3d_side_by_side(int stereo_mode, int div[2])
}
void mpgl_osd_draw_finish(struct mpgl_osd *ctx, int index,
- struct gl_shader_cache *sc, struct ra_fbo fbo)
+ struct gl_shader_cache *sc, const struct ra_fbo *fbo)
{
struct mpgl_osd_part *part = ctx->parts[index];
@@ -312,7 +312,7 @@ void mpgl_osd_draw_finish(struct mpgl_osd *ctx, int index,
const int *factors = &blend_factors[part->format][0];
gl_sc_blend(sc, factors[0], factors[1], factors[2], factors[3]);
- gl_sc_dispatch_draw(sc, fbo.tex, false, vertex_vao, MP_ARRAY_SIZE(vertex_vao),
+ gl_sc_dispatch_draw(sc, fbo->tex, false, vertex_vao, MP_ARRAY_SIZE(vertex_vao),
sizeof(struct vertex), part->vertices, part->num_vertices);
}
diff --git a/video/out/gpu/osd.h b/video/out/gpu/osd.h
index 00fbc49..1b05e25 100644
--- a/video/out/gpu/osd.h
+++ b/video/out/gpu/osd.h
@@ -18,7 +18,7 @@ void mpgl_osd_resize(struct mpgl_osd *ctx, struct mp_osd_res res, int stereo_mod
bool mpgl_osd_draw_prepare(struct mpgl_osd *ctx, int index,
struct gl_shader_cache *sc);
void mpgl_osd_draw_finish(struct mpgl_osd *ctx, int index,
- struct gl_shader_cache *sc, struct ra_fbo fbo);
+ struct gl_shader_cache *sc, const struct ra_fbo *fbo);
bool mpgl_osd_check_change(struct mpgl_osd *ctx, struct mp_osd_res *res,
double pts);
diff --git a/video/out/gpu/ra.h b/video/out/gpu/ra.h
index 5f229f8..c0c58ac 100644
--- a/video/out/gpu/ra.h
+++ b/video/out/gpu/ra.h
@@ -143,8 +143,8 @@ struct ra_tex_params {
// be true depends on ra_format.linear_filter)
bool src_repeat; // if false, clamp texture coordinates to edge
// if true, repeat texture coordinates
- bool non_normalized; // hack for GL_TEXTURE_RECTANGLE OSX idiocy
- // always set to false, except in OSX code
+ bool non_normalized; // hack for GL_TEXTURE_RECTANGLE macOS idiocy
+ // always set to false, except in macOS code
bool external_oes; // hack for GL_TEXTURE_EXTERNAL_OES idiocy
// If non-NULL, the texture will be created with these contents. Using
// this does *not* require setting host_mutable. Otherwise, the initial
diff --git a/video/out/gpu/spirv.c b/video/out/gpu/spirv.c
index 67088bc..6910049 100644
--- a/video/out/gpu/spirv.c
+++ b/video/out/gpu/spirv.c
@@ -16,6 +16,7 @@ static const struct spirv_compiler_fns *compilers[] = {
#if HAVE_SHADERC
[SPIRV_SHADERC] = &spirv_shaderc,
#endif
+ NULL
};
static const struct m_opt_choice_alternatives compiler_choices[] = {
diff --git a/video/out/gpu/user_shaders.c b/video/out/gpu/user_shaders.c
index 708de87..f2507b6 100644
--- a/video/out/gpu/user_shaders.c
+++ b/video/out/gpu/user_shaders.c
@@ -431,7 +431,7 @@ static bool parse_tex(struct mp_log *log, struct ra *ra, struct bstr *body,
void parse_user_shader(struct mp_log *log, struct ra *ra, struct bstr shader,
void *priv,
- bool (*dohook)(void *p, struct gl_user_shader_hook hook),
+ bool (*dohook)(void *p, const struct gl_user_shader_hook *hook),
bool (*dotex)(void *p, struct gl_user_shader_tex tex))
{
if (!dohook || !dotex || !shader.len)
@@ -457,7 +457,7 @@ void parse_user_shader(struct mp_log *log, struct ra *ra, struct bstr shader,
}
struct gl_user_shader_hook h;
- if (!parse_hook(log, &shader, &h) || !dohook(priv, h))
+ if (!parse_hook(log, &shader, &h) || !dohook(priv, &h))
return;
}
}
diff --git a/video/out/gpu/user_shaders.h b/video/out/gpu/user_shaders.h
index 4bb7c22..d3405a8 100644
--- a/video/out/gpu/user_shaders.h
+++ b/video/out/gpu/user_shaders.h
@@ -88,7 +88,7 @@ struct gl_user_shader_tex {
// valid shader block parsed.
void parse_user_shader(struct mp_log *log, struct ra *ra, struct bstr shader,
void *priv,
- bool (*dohook)(void *p, struct gl_user_shader_hook hook),
+ bool (*dohook)(void *p, const struct gl_user_shader_hook *hook),
bool (*dotex)(void *p, struct gl_user_shader_tex tex));
// Evaluate a szexp, given a lookup function for named textures
diff --git a/video/out/gpu/utils.c b/video/out/gpu/utils.c
index 8a1aacf..d18cf6e 100644
--- a/video/out/gpu/utils.c
+++ b/video/out/gpu/utils.c
@@ -33,10 +33,10 @@ void gl_transform_trans(struct gl_transform t, struct gl_transform *x)
gl_transform_vec(t, &x->t[0], &x->t[1]);
}
-void gl_transform_ortho_fbo(struct gl_transform *t, struct ra_fbo fbo)
+void gl_transform_ortho_fbo(struct gl_transform *t, const struct ra_fbo *fbo)
{
- int y_dir = fbo.flip ? -1 : 1;
- gl_transform_ortho(t, 0, fbo.tex->params.w, 0, fbo.tex->params.h * y_dir);
+ int y_dir = fbo->flip ? -1 : 1;
+ gl_transform_ortho(t, 0, fbo->tex->params.w, 0, fbo->tex->params.h * y_dir);
}
float gl_video_scale_ambient_lux(float lmin, float lmax,
diff --git a/video/out/gpu/utils.h b/video/out/gpu/utils.h
index 215873e..dd52c38 100644
--- a/video/out/gpu/utils.h
+++ b/video/out/gpu/utils.h
@@ -63,7 +63,7 @@ static inline bool gl_transform_eq(struct gl_transform a, struct gl_transform b)
void gl_transform_trans(struct gl_transform t, struct gl_transform *x);
-void gl_transform_ortho_fbo(struct gl_transform *t, struct ra_fbo fbo);
+void gl_transform_ortho_fbo(struct gl_transform *t, const struct ra_fbo *fbo);
float gl_video_scale_ambient_lux(float lmin, float lmax,
float rmin, float rmax, float lux);
diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c
index 852ee78..1478ec4 100644
--- a/video/out/gpu/video.c
+++ b/video/out/gpu/video.c
@@ -183,6 +183,7 @@ struct gl_video {
struct mp_image_params real_image_params; // configured format
struct mp_image_params image_params; // texture format (mind hwdec case)
+ struct mp_image_params target_params; // target format
struct ra_imgfmt_desc ra_format; // texture format
int plane_count;
@@ -212,6 +213,7 @@ struct gl_video {
struct ra_tex *merge_tex[4];
struct ra_tex *scale_tex[4];
struct ra_tex *integer_tex[4];
+ struct ra_tex *chroma_tex[4];
struct ra_tex *indirect_tex;
struct ra_tex *blend_subs_tex;
struct ra_tex *error_diffusion_tex[2];
@@ -312,8 +314,8 @@ static const struct gl_video_opts gl_video_opts_def = {
.linear_downscaling = true,
.sigmoid_upscaling = true,
.interpolation_threshold = 0.01,
- .alpha_mode = ALPHA_BLEND_TILES,
- .background = {0, 0, 0, 255},
+ .background = BACKGROUND_TILES,
+ .background_color = {0, 0, 0, 255},
.gamma = 1.0f,
.tone_map = {
.curve = TONE_MAPPING_AUTO,
@@ -329,14 +331,9 @@ static const struct gl_video_opts gl_video_opts_def = {
.hwdec_interop = "auto",
};
-static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, const char **value);
-
-static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, const char **value);
-
-static int validate_error_diffusion_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, const char **value);
+static OPT_STRING_VALIDATE_FUNC(validate_scaler_opt);
+static OPT_STRING_VALIDATE_FUNC(validate_window_opt);
+static OPT_STRING_VALIDATE_FUNC(validate_error_diffusion_opt);
#define OPT_BASE_STRUCT struct gl_video_opts
@@ -368,13 +365,13 @@ const struct m_sub_options gl_video_conf = {
.deprecation_message = "no replacement"},
{"gamma-auto", OPT_BOOL(gamma_auto),
.deprecation_message = "no replacement"},
- {"target-prim", OPT_CHOICE_C(target_prim, mp_csp_prim_names)},
- {"target-trc", OPT_CHOICE_C(target_trc, mp_csp_trc_names)},
+ {"target-prim", OPT_CHOICE_C(target_prim, pl_csp_prim_names)},
+ {"target-trc", OPT_CHOICE_C(target_trc, pl_csp_trc_names)},
{"target-peak", OPT_CHOICE(target_peak, {"auto", 0}),
M_RANGE(10, 10000)},
{"target-contrast", OPT_CHOICE(target_contrast, {"auto", 0}, {"inf", -1}),
M_RANGE(10, 1000000)},
- {"target-gamut", OPT_CHOICE_C(target_gamut, mp_csp_prim_names)},
+ {"target-gamut", OPT_CHOICE_C(target_gamut, pl_csp_prim_names)},
{"tone-mapping", OPT_CHOICE(tone_map.curve,
{"auto", TONE_MAPPING_AUTO},
{"clip", TONE_MAPPING_CLIP},
@@ -447,13 +444,12 @@ const struct m_sub_options gl_video_conf = {
M_RANGE(1, 128)},
{"error-diffusion",
OPT_STRING_VALIDATE(error_diffusion, validate_error_diffusion_opt)},
- {"alpha", OPT_CHOICE(alpha_mode,
- {"no", ALPHA_NO},
- {"yes", ALPHA_YES},
- {"blend", ALPHA_BLEND},
- {"blend-tiles", ALPHA_BLEND_TILES})},
+ {"background", OPT_CHOICE(background,
+ {"none", BACKGROUND_NONE},
+ {"color", BACKGROUND_COLOR},
+ {"tiles", BACKGROUND_TILES})},
{"opengl-rectangle-textures", OPT_BOOL(use_rectangle)},
- {"background", OPT_COLOR(background)},
+ {"background-color", OPT_COLOR(background_color)},
{"interpolation", OPT_BOOL(interpolation)},
{"interpolation-threshold", OPT_FLOAT(interpolation_threshold)},
{"blend-subtitles", OPT_CHOICE(blend_subs,
@@ -483,6 +479,7 @@ const struct m_sub_options gl_video_conf = {
},
.size = sizeof(struct gl_video_opts),
.defaults = &gl_video_opts_def,
+ .change_flags = UPDATE_VIDEO,
};
static void uninit_rendering(struct gl_video *p);
@@ -578,6 +575,7 @@ static void uninit_rendering(struct gl_video *p)
ra_tex_free(p->ra, &p->merge_tex[n]);
ra_tex_free(p->ra, &p->scale_tex[n]);
ra_tex_free(p->ra, &p->integer_tex[n]);
+ ra_tex_free(p->ra, &p->chroma_tex[n]);
}
ra_tex_free(p->ra, &p->indirect_tex);
@@ -605,15 +603,6 @@ bool gl_video_gamma_auto_enabled(struct gl_video *p)
return p->opts.gamma_auto;
}
-struct mp_colorspace gl_video_get_output_colorspace(struct gl_video *p)
-{
- return (struct mp_colorspace) {
- .primaries = p->opts.target_prim,
- .gamma = p->opts.target_trc,
- .hdr.max_luma = p->opts.target_peak,
- };
-}
-
// Warning: profile.start must point to a ta allocation, and the function
// takes over ownership.
void gl_video_set_icc_profile(struct gl_video *p, bstr icc_data)
@@ -627,8 +616,8 @@ bool gl_video_icc_auto_enabled(struct gl_video *p)
return p->opts.icc_opts ? p->opts.icc_opts->profile_auto : false;
}
-static bool gl_video_get_lut3d(struct gl_video *p, enum mp_csp_prim prim,
- enum mp_csp_trc trc)
+static bool gl_video_get_lut3d(struct gl_video *p, enum pl_color_primaries prim,
+ enum pl_color_transfer trc)
{
if (!p->use_lut_3d)
return false;
@@ -771,16 +760,16 @@ static void pass_get_images(struct gl_video *p, struct video_image *vimg,
struct gl_transform chroma = {{{ls_w, 0.0}, {0.0, ls_h}}};
- if (p->image_params.chroma_location != MP_CHROMA_CENTER) {
- int cx, cy;
- mp_get_chroma_location(p->image_params.chroma_location, &cx, &cy);
+ if (p->image_params.chroma_location != PL_CHROMA_CENTER) {
+ float cx, cy;
+ pl_chroma_location_offset(p->image_params.chroma_location, &cx, &cy);
// By default texture coordinates are such that chroma is centered with
// any chroma subsampling. If a specific direction is given, make it
// so that the luma and chroma sample line up exactly.
// For 4:4:4, setting chroma location should have no effect at all.
// luma sample size (in chroma coord. space)
- chroma.t[0] = ls_w < 1 ? ls_w * -cx / 2 : 0;
- chroma.t[1] = ls_h < 1 ? ls_h * -cy / 2 : 0;
+ chroma.t[0] = ls_w < 1 ? ls_w * -cx : 0;
+ chroma.t[1] = ls_h < 1 ? ls_h * -cy : 0;
}
memset(img, 0, 4 * sizeof(img[0]));
@@ -796,9 +785,9 @@ static void pass_get_images(struct gl_video *p, struct video_image *vimg,
ctype = PLANE_NONE;
} else if (c == 4) {
ctype = PLANE_ALPHA;
- } else if (p->image_params.color.space == MP_CSP_RGB) {
+ } else if (p->image_params.repr.sys == PL_COLOR_SYSTEM_RGB) {
ctype = PLANE_RGB;
- } else if (p->image_params.color.space == MP_CSP_XYZ) {
+ } else if (p->image_params.repr.sys == PL_COLOR_SYSTEM_XYZ) {
ctype = PLANE_XYZ;
} else {
ctype = c == 1 ? PLANE_LUMA : PLANE_CHROMA;
@@ -810,7 +799,7 @@ static void pass_get_images(struct gl_video *p, struct video_image *vimg,
int msb_valid_bits =
p->ra_format.component_bits + MPMIN(p->ra_format.component_pad, 0);
- int csp = type == PLANE_ALPHA ? MP_CSP_RGB : p->image_params.color.space;
+ int csp = type == PLANE_ALPHA ? PL_COLOR_SYSTEM_RGB : p->image_params.repr.sys;
float tex_mul =
1.0 / mp_get_csp_mul(csp, msb_valid_bits, p->ra_format.component_bits);
if (p->ra_format.component_type == RA_CTYPE_FLOAT)
@@ -1065,13 +1054,13 @@ static void uninit_video(struct gl_video *p)
ra_hwdec_mapper_free(&p->hwdec_mapper);
}
-static void pass_record(struct gl_video *p, struct mp_pass_perf perf)
+static void pass_record(struct gl_video *p, const struct mp_pass_perf *perf)
{
if (!p->pass || p->pass_idx == VO_PASS_PERF_MAX)
return;
struct pass_info *pass = &p->pass[p->pass_idx];
- pass->perf = perf;
+ pass->perf = *perf;
if (pass->desc.len == 0)
bstr_xappend(p, &pass->desc, bstr0("(unknown)"));
@@ -1211,12 +1200,13 @@ static void dispatch_compute(struct gl_video *p, int w, int h,
if (!(p->ra->caps & RA_CAP_NUM_GROUPS))
PRELUDE("#define gl_NumWorkGroups uvec3(%d, %d, 1)\n", num_x, num_y);
- pass_record(p, gl_sc_dispatch_compute(p->sc, num_x, num_y, 1));
+ struct mp_pass_perf perf = gl_sc_dispatch_compute(p->sc, num_x, num_y, 1);
+ pass_record(p, &perf);
cleanup_binds(p);
}
static struct mp_pass_perf render_pass_quad(struct gl_video *p,
- struct ra_fbo fbo, bool discard,
+ const struct ra_fbo *fbo, bool discard,
const struct mp_rect *dst)
{
// The first element is reserved for `vec2 position`
@@ -1274,15 +1264,16 @@ static struct mp_pass_perf render_pass_quad(struct gl_video *p,
&p->tmp_vertex[num_vertex_attribs * 1],
vertex_stride);
- return gl_sc_dispatch_draw(p->sc, fbo.tex, discard, p->vao, num_vertex_attribs,
+ return gl_sc_dispatch_draw(p->sc, fbo->tex, discard, p->vao, num_vertex_attribs,
vertex_stride, p->tmp_vertex, num_vertices);
}
-static void finish_pass_fbo(struct gl_video *p, struct ra_fbo fbo,
+static void finish_pass_fbo(struct gl_video *p, const struct ra_fbo *fbo,
bool discard, const struct mp_rect *dst)
{
pass_prepare_src_tex(p);
- pass_record(p, render_pass_quad(p, fbo, discard, dst));
+ struct mp_pass_perf perf = render_pass_quad(p, fbo, discard, dst);
+ pass_record(p, &perf);
debug_check_gl(p, "after rendering");
cleanup_binds(p);
}
@@ -1319,7 +1310,7 @@ static void finish_pass_tex(struct gl_video *p, struct ra_tex **dst_tex,
debug_check_gl(p, "after dispatching compute shader");
} else {
struct ra_fbo fbo = { .tex = *dst_tex, };
- finish_pass_fbo(p, fbo, true, &(struct mp_rect){0, 0, w, h});
+ finish_pass_fbo(p, &fbo, true, &(struct mp_rect){0, 0, w, h});
}
}
@@ -1955,7 +1946,7 @@ static void deband_hook(struct gl_video *p, struct image img,
{
pass_describe(p, "debanding (%s)", plane_names[img.type]);
pass_sample_deband(p->sc, p->opts.deband_opts, &p->lfg,
- p->image_params.color.gamma);
+ p->image_params.color.transfer);
}
static void unsharp_hook(struct gl_video *p, struct image img,
@@ -2046,25 +2037,23 @@ static void user_hook(struct gl_video *p, struct image img,
gl_transform_trans(shader->offset, trans);
}
-static bool add_user_hook(void *priv, struct gl_user_shader_hook hook)
+static bool add_user_hook(void *priv, const struct gl_user_shader_hook *hook)
{
struct gl_video *p = priv;
- struct gl_user_shader_hook *copy = talloc_ptrtype(p, copy);
- *copy = hook;
-
+ struct gl_user_shader_hook *copy = talloc_dup(p, (struct gl_user_shader_hook *)hook);
struct tex_hook texhook = {
- .save_tex = bstrdup0(copy, hook.save_tex),
- .components = hook.components,
- .align_offset = hook.align_offset,
+ .save_tex = bstrdup0(copy, copy->save_tex),
+ .components = copy->components,
+ .align_offset = copy->align_offset,
.hook = user_hook,
.cond = user_hook_cond,
.priv = copy,
};
for (int h = 0; h < SHADER_MAX_HOOKS; h++)
- texhook.hook_tex[h] = bstrdup0(copy, hook.hook_tex[h]);
+ texhook.hook_tex[h] = bstrdup0(copy, copy->hook_tex[h]);
for (int h = 0; h < SHADER_MAX_BINDS; h++)
- texhook.bind_tex[h] = bstrdup0(copy, hook.bind_tex[h]);
+ texhook.bind_tex[h] = bstrdup0(copy, copy->bind_tex[h]);
MP_TARRAY_APPEND(p, p->tex_hooks, p->num_tex_hooks, texhook);
return true;
@@ -2213,6 +2202,23 @@ static void pass_read_video(struct gl_video *p)
}
}
+ // If chroma textures are in a subsampled semi-planar format and rotated,
+ // introduce an explicit conversion pass to avoid breaking chroma scalers.
+ for (int n = 0; n < 4; n++) {
+ if (img[n].tex && img[n].type == PLANE_CHROMA &&
+ img[n].tex->params.format->num_components == 2 &&
+ p->image_params.rotate % 180 == 90 &&
+ p->ra_format.chroma_w != 1)
+ {
+ GLSLF("// chroma fix for rotated plane %d\n", n);
+ copy_image(p, &(int){0}, img[n]);
+ pass_describe(p, "chroma fix for rotated plane");
+ finish_pass_tex(p, &p->chroma_tex[n], img[n].w, img[n].h);
+ img[n] = image_wrap(p->chroma_tex[n], img[n].type,
+ img[n].components);
+ }
+ }
+
// At this point all planes are finalized but they may not be at the
// required size yet. Furthermore, they may have texture offsets that
// require realignment.
@@ -2345,29 +2351,29 @@ static void pass_convert_yuv(struct gl_video *p)
GLSLF("color = color.%s;\n", p->color_swizzle);
// Pre-colormatrix input gamma correction
- if (cparams.color.space == MP_CSP_XYZ)
- pass_linearize(p->sc, p->image_params.color.gamma);
+ if (cparams.repr.sys == PL_COLOR_SYSTEM_XYZ)
+ pass_linearize(p->sc, p->image_params.color.transfer);
// We always explicitly normalize the range in pass_read_video
cparams.input_bits = cparams.texture_bits = 0;
// Conversion to RGB. For RGB itself, this still applies e.g. brightness
// and contrast controls, or expansion of e.g. LSB-packed 10 bit data.
- struct mp_cmat m = {{{0}}};
+ struct pl_transform3x3 m = {0};
mp_get_csp_matrix(&cparams, &m);
- gl_sc_uniform_mat3(sc, "colormatrix", true, &m.m[0][0]);
+ gl_sc_uniform_mat3(sc, "colormatrix", true, &m.mat.m[0][0]);
gl_sc_uniform_vec3(sc, "colormatrix_c", m.c);
GLSL(color.rgb = mat3(colormatrix) * color.rgb + colormatrix_c;)
- if (cparams.color.space == MP_CSP_XYZ) {
- pass_delinearize(p->sc, p->image_params.color.gamma);
+ if (cparams.repr.sys == PL_COLOR_SYSTEM_XYZ) {
+ pass_delinearize(p->sc, p->image_params.color.transfer);
// mp_get_csp_matrix implicitly converts XYZ to DCI-P3
- p->image_params.color.space = MP_CSP_RGB;
- p->image_params.color.primaries = MP_CSP_PRIM_DCI_P3;
+ p->image_params.repr.sys = PL_COLOR_SYSTEM_RGB;
+ p->image_params.color.primaries = PL_COLOR_PRIM_DCI_P3;
}
- if (p->image_params.color.space == MP_CSP_BT_2020_C) {
+ if (p->image_params.repr.sys == PL_COLOR_SYSTEM_BT_2020_C) {
// Conversion for C'rcY'cC'bc via the BT.2020 CL system:
// C'bc = (B'-Y'c) / 1.9404 | C'bc <= 0
// = (B'-Y'c) / 1.5816 | C'bc > 0
@@ -2404,9 +2410,9 @@ static void pass_convert_yuv(struct gl_video *p)
}
p->components = 3;
- if (!p->has_alpha || p->opts.alpha_mode == ALPHA_NO) {
+ if (!p->has_alpha) {
GLSL(color.a = 1.0;)
- } else if (p->image_params.alpha == MP_ALPHA_PREMUL) {
+ } else if (p->image_params.repr.alpha == PL_ALPHA_PREMULTIPLIED) {
p->components = 4;
} else {
p->components = 4;
@@ -2491,7 +2497,7 @@ static void pass_scale_main(struct gl_video *p)
// Linear light downscaling results in nasty artifacts for HDR curves
// due to the potentially extreme brightness differences severely
// compounding any ringing. So just scale in gamma light instead.
- if (mp_trc_is_hdr(p->image_params.color.gamma))
+ if (pl_color_space_is_hdr(&p->image_params.color))
use_linear = false;
} else if (upscaling) {
use_linear = p->opts.linear_upscaling || p->opts.sigmoid_upscaling;
@@ -2499,7 +2505,7 @@ static void pass_scale_main(struct gl_video *p)
if (use_linear) {
p->use_linear = true;
- pass_linearize(p->sc, p->image_params.color.gamma);
+ pass_linearize(p->sc, p->image_params.color.transfer);
pass_opt_hook_point(p, "LINEAR", NULL);
}
@@ -2552,8 +2558,9 @@ static void pass_scale_main(struct gl_video *p)
// rendering)
// If OSD is true, ignore any changes that may have been made to the video
// by previous passes (i.e. linear scaling)
-static void pass_colormanage(struct gl_video *p, struct mp_colorspace src,
- struct mp_colorspace fbo_csp, int flags, bool osd)
+static void pass_colormanage(struct gl_video *p, struct pl_color_space src,
+ enum mp_csp_light src_light,
+ struct pl_color_space fbo_csp, int flags, bool osd)
{
struct ra *ra = p->ra;
@@ -2561,18 +2568,17 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src,
// unless specific transfer function, primaries or target peak
// is set. If values are set to _AUTO, the most likely intended
// values are guesstimated later in this function.
- struct mp_colorspace dst = {
- .gamma = p->opts.target_trc == MP_CSP_TRC_AUTO ?
- fbo_csp.gamma : p->opts.target_trc,
- .primaries = p->opts.target_prim == MP_CSP_PRIM_AUTO ?
+ struct pl_color_space dst = {
+ .transfer = p->opts.target_trc == PL_COLOR_TRC_UNKNOWN ?
+ fbo_csp.transfer : p->opts.target_trc,
+ .primaries = p->opts.target_prim == PL_COLOR_PRIM_UNKNOWN ?
fbo_csp.primaries : p->opts.target_prim,
- .light = MP_CSP_LIGHT_DISPLAY,
.hdr.max_luma = !p->opts.target_peak ?
fbo_csp.hdr.max_luma : p->opts.target_peak,
};
if (!p->colorspace_override_warned &&
- ((fbo_csp.gamma && dst.gamma != fbo_csp.gamma) ||
+ ((fbo_csp.transfer && dst.transfer != fbo_csp.transfer) ||
(fbo_csp.primaries && dst.primaries != fbo_csp.primaries)))
{
MP_WARN(p, "One or more colorspace value is being overridden "
@@ -2580,44 +2586,44 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src,
"transfer function: (dst: %s, fbo: %s), "
"primaries: (dst: %s, fbo: %s). "
"Rendering can lead to incorrect results!\n",
- m_opt_choice_str(mp_csp_trc_names, dst.gamma),
- m_opt_choice_str(mp_csp_trc_names, fbo_csp.gamma),
- m_opt_choice_str(mp_csp_prim_names, dst.primaries),
- m_opt_choice_str(mp_csp_prim_names, fbo_csp.primaries));
+ m_opt_choice_str(pl_csp_trc_names, dst.transfer),
+ m_opt_choice_str(pl_csp_trc_names, fbo_csp.transfer),
+ m_opt_choice_str(pl_csp_prim_names, dst.primaries),
+ m_opt_choice_str(pl_csp_prim_names, fbo_csp.primaries));
p->colorspace_override_warned = true;
}
- if (dst.gamma == MP_CSP_TRC_HLG)
- dst.light = MP_CSP_LIGHT_SCENE_HLG;
+ enum mp_csp_light dst_light = dst.transfer == PL_COLOR_TRC_HLG ?
+ MP_CSP_LIGHT_SCENE_HLG : MP_CSP_LIGHT_DISPLAY;
if (p->use_lut_3d && (flags & RENDER_SCREEN_COLOR)) {
// The 3DLUT is always generated against the video's original source
// space, *not* the reference space. (To avoid having to regenerate
// the 3DLUT for the OSD on every frame)
- enum mp_csp_prim prim_orig = p->image_params.color.primaries;
- enum mp_csp_trc trc_orig = p->image_params.color.gamma;
+ enum pl_color_primaries prim_orig = p->image_params.color.primaries;
+ enum pl_color_transfer trc_orig = p->image_params.color.transfer;
// One exception: HDR is not implemented by LittleCMS for technical
// limitation reasons, so we use a gamma 2.2 input curve here instead.
// We could pick any value we want here, the difference is just coding
// efficiency.
- if (mp_trc_is_hdr(trc_orig))
- trc_orig = MP_CSP_TRC_GAMMA22;
+ if (pl_color_space_is_hdr(&p->image_params.color))
+ trc_orig = PL_COLOR_TRC_GAMMA22;
if (gl_video_get_lut3d(p, prim_orig, trc_orig)) {
dst.primaries = prim_orig;
- dst.gamma = trc_orig;
- assert(dst.primaries && dst.gamma);
+ dst.transfer = trc_orig;
+ assert(dst.primaries && dst.transfer);
}
}
- if (dst.primaries == MP_CSP_PRIM_AUTO) {
+ if (dst.primaries == PL_COLOR_PRIM_UNKNOWN) {
// The vast majority of people are on sRGB or BT.709 displays, so pick
// this as the default output color space.
- dst.primaries = MP_CSP_PRIM_BT_709;
+ dst.primaries = PL_COLOR_PRIM_BT_709;
- if (src.primaries == MP_CSP_PRIM_BT_601_525 ||
- src.primaries == MP_CSP_PRIM_BT_601_625)
+ if (src.primaries == PL_COLOR_PRIM_BT_601_525 ||
+ src.primaries == PL_COLOR_PRIM_BT_601_625)
{
// Since we auto-pick BT.601 and BT.709 based on the dimensions,
// combined with the fact that they're very similar to begin with,
@@ -2627,28 +2633,28 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src,
}
}
- if (dst.gamma == MP_CSP_TRC_AUTO) {
+ if (dst.transfer == PL_COLOR_TRC_UNKNOWN) {
// Most people seem to complain when the image is darker or brighter
// than what they're "used to", so just avoid changing the gamma
// altogether by default. The only exceptions to this rule apply to
// very unusual TRCs, which even hardcode technoluddites would probably
// not enjoy viewing unaltered.
- dst.gamma = src.gamma;
+ dst.transfer = src.transfer;
// Avoid outputting linear light or HDR content "by default". For these
// just pick gamma 2.2 as a default, since it's a good estimate for
// the response of typical displays
- if (dst.gamma == MP_CSP_TRC_LINEAR || mp_trc_is_hdr(dst.gamma))
- dst.gamma = MP_CSP_TRC_GAMMA22;
+ if (dst.transfer == PL_COLOR_TRC_LINEAR || pl_color_space_is_hdr(&dst))
+ dst.transfer = PL_COLOR_TRC_GAMMA22;
}
// If there's no specific signal peak known for the output display, infer
// it from the chosen transfer function. Also normalize the src peak, in
// case it was unknown
if (!dst.hdr.max_luma)
- dst.hdr.max_luma = mp_trc_nom_peak(dst.gamma) * MP_REF_WHITE;
+ dst.hdr.max_luma = pl_color_transfer_nominal_peak(dst.transfer) * MP_REF_WHITE;
if (!src.hdr.max_luma)
- src.hdr.max_luma = mp_trc_nom_peak(src.gamma) * MP_REF_WHITE;
+ src.hdr.max_luma = pl_color_transfer_nominal_peak(src.transfer) * MP_REF_WHITE;
// Whitelist supported modes
switch (p->opts.tone_map.curve) {
@@ -2680,7 +2686,7 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src,
}
struct gl_tone_map_opts tone_map = p->opts.tone_map;
- bool detect_peak = tone_map.compute_peak >= 0 && mp_trc_is_hdr(src.gamma)
+ bool detect_peak = tone_map.compute_peak >= 0 && pl_color_space_is_hdr(&src)
&& src.hdr.max_luma > dst.hdr.max_luma;
if (detect_peak && !p->hdr_peak_ssbo) {
@@ -2719,7 +2725,22 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src,
}
// Adapt from src to dst as necessary
- pass_color_map(p->sc, p->use_linear && !osd, src, dst, &tone_map);
+ pass_color_map(p->sc, p->use_linear && !osd, src, dst, src_light, dst_light, &tone_map);
+
+ if (!osd) {
+ struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS;
+ mp_csp_equalizer_state_get(p->video_eq, &cparams);
+ if (cparams.levels_out == PL_COLOR_LEVELS_UNKNOWN)
+ cparams.levels_out = PL_COLOR_LEVELS_FULL;
+ p->target_params = (struct mp_image_params){
+ .imgfmt_name = p->fbo_format ? p->fbo_format->name : "unknown",
+ .w = p->texture_w,
+ .h = p->texture_h,
+ .color = dst,
+ .repr = {.sys = PL_COLOR_SYSTEM_RGB, .levels = cparams.levels_out},
+ .rotate = p->image_params.rotate,
+ };
+ }
if (p->use_lut_3d && (flags & RENDER_SCREEN_COLOR)) {
gl_sc_uniform_texture(p->sc, "lut_3d", p->lut_3d_texture);
@@ -2735,7 +2756,7 @@ void gl_video_set_fb_depth(struct gl_video *p, int fb_depth)
p->fb_depth = fb_depth;
}
-static void pass_dither(struct gl_video *p)
+static void pass_dither(struct gl_video *p, const struct ra_fbo *fbo)
{
// Assume 8 bits per component if unknown.
int dst_depth = p->fb_depth > 0 ? p->fb_depth : 8;
@@ -2868,7 +2889,9 @@ static void pass_dither(struct gl_video *p)
gl_sc_uniform_texture(p->sc, "dither", p->dither_texture);
- GLSLF("vec2 dither_pos = gl_FragCoord.xy * 1.0/%d.0;\n", dither_size);
+ GLSLF("vec2 dither_coord = vec2(gl_FragCoord.x, %d.0 + %f * gl_FragCoord.y);",
+ fbo->flip ? fbo->tex->params.h : 0, fbo->flip ? -1.0 : 1.0);
+ GLSLF("vec2 dither_pos = dither_coord * 1.0/%d.0;\n", dither_size);
if (p->opts.temporal_dither) {
int phase = (p->frames_rendered / p->opts.temporal_dither_period) % 8u;
@@ -2891,7 +2914,7 @@ static void pass_dither(struct gl_video *p)
// Draws the OSD, in scene-referred colors.. If cms is true, subtitles are
// instead adapted to the display's gamut.
static void pass_draw_osd(struct gl_video *p, int osd_flags, int frame_flags,
- double pts, struct mp_osd_res rect, struct ra_fbo fbo,
+ double pts, struct mp_osd_res rect, const struct ra_fbo *fbo,
bool cms)
{
if (frame_flags & RENDER_FRAME_VF_SUBS)
@@ -2910,20 +2933,21 @@ static void pass_draw_osd(struct gl_video *p, int osd_flags, int frame_flags,
// When subtitles need to be color managed, assume they're in sRGB
// (for lack of anything saner to do)
if (cms) {
- static const struct mp_colorspace csp_srgb = {
- .primaries = MP_CSP_PRIM_BT_709,
- .gamma = MP_CSP_TRC_SRGB,
- .light = MP_CSP_LIGHT_DISPLAY,
+ static const struct pl_color_space csp_srgb = {
+ .primaries = PL_COLOR_PRIM_BT_709,
+ .transfer = PL_COLOR_TRC_SRGB,
};
- pass_colormanage(p, csp_srgb, fbo.color_space, frame_flags, true);
+ pass_colormanage(p, csp_srgb, MP_CSP_LIGHT_DISPLAY, fbo->color_space,
+ frame_flags, true);
}
mpgl_osd_draw_finish(p->osd, n, p->sc, fbo);
}
timer_pool_stop(p->osd_timer);
pass_describe(p, "drawing osd");
- pass_record(p, timer_pool_measure(p->osd_timer));
+ struct mp_pass_perf perf = timer_pool_measure(p->osd_timer);
+ pass_record(p, &perf);
}
static float chroma_realign(int size, int pixel)
@@ -3013,7 +3037,7 @@ static bool pass_render_frame(struct gl_video *p, struct mp_image *mpi,
};
finish_pass_tex(p, &p->blend_subs_tex, rect.w, rect.h);
struct ra_fbo fbo = { p->blend_subs_tex };
- pass_draw_osd(p, OSD_DRAW_SUB_ONLY, flags, vpts, rect, fbo, false);
+ pass_draw_osd(p, OSD_DRAW_SUB_ONLY, flags, vpts, rect, &fbo, false);
pass_read_tex(p, p->blend_subs_tex);
pass_describe(p, "blend subs video");
}
@@ -3040,12 +3064,12 @@ static bool pass_render_frame(struct gl_video *p, struct mp_image *mpi,
rect.mt *= scale[1]; rect.mb *= scale[1];
// We should always blend subtitles in non-linear light
if (p->use_linear) {
- pass_delinearize(p->sc, p->image_params.color.gamma);
+ pass_delinearize(p->sc, p->image_params.color.transfer);
p->use_linear = false;
}
finish_pass_tex(p, &p->blend_subs_tex, p->texture_w, p->texture_h);
struct ra_fbo fbo = { p->blend_subs_tex };
- pass_draw_osd(p, OSD_DRAW_SUB_ONLY, flags, vpts, rect, fbo, false);
+ pass_draw_osd(p, OSD_DRAW_SUB_ONLY, flags, vpts, rect, &fbo, false);
pass_read_tex(p, p->blend_subs_tex);
pass_describe(p, "blend subs");
}
@@ -3055,7 +3079,7 @@ static bool pass_render_frame(struct gl_video *p, struct mp_image *mpi,
return true;
}
-static void pass_draw_to_screen(struct gl_video *p, struct ra_fbo fbo, int flags)
+static void pass_draw_to_screen(struct gl_video *p, const struct ra_fbo *fbo, int flags)
{
if (p->dumb_mode)
pass_render_frame_dumb(p);
@@ -3067,7 +3091,8 @@ static void pass_draw_to_screen(struct gl_video *p, struct ra_fbo fbo, int flags
GLSL(color.rgb = pow(color.rgb, vec3(user_gamma));)
}
- pass_colormanage(p, p->image_params.color, fbo.color_space, flags, false);
+ pass_colormanage(p, p->image_params.color, p->image_params.light,
+ fbo->color_space, flags, false);
// Since finish_pass_fbo doesn't work with compute shaders, and neither
// does the checkerboard/dither code, we may need an indirection via
@@ -3080,28 +3105,30 @@ static void pass_draw_to_screen(struct gl_video *p, struct ra_fbo fbo, int flags
copy_image(p, &(int){0}, tmp);
}
- if (p->has_alpha){
- if (p->opts.alpha_mode == ALPHA_BLEND_TILES) {
+ if (p->has_alpha) {
+ if (p->opts.background == BACKGROUND_TILES) {
// Draw checkerboard pattern to indicate transparency
GLSLF("// transparency checkerboard\n");
- GLSL(bvec2 tile = lessThan(fract(gl_FragCoord.xy * 1.0/32.0), vec2(0.5));)
+ GLSLF("vec2 tile_coord = vec2(gl_FragCoord.x, %d.0 + %f * gl_FragCoord.y);",
+ fbo->flip ? fbo->tex->params.h : 0, fbo->flip ? -1.0 : 1.0);
+ GLSL(bvec2 tile = lessThan(fract(tile_coord * 1.0 / 32.0), vec2(0.5));)
GLSL(vec3 background = vec3(tile.x == tile.y ? 0.93 : 0.87);)
GLSL(color.rgb += background.rgb * (1.0 - color.a);)
GLSL(color.a = 1.0;)
- } else if (p->opts.alpha_mode == ALPHA_BLEND) {
+ } else if (p->opts.background == BACKGROUND_COLOR) {
// Blend into background color (usually black)
- struct m_color c = p->opts.background;
+ struct m_color c = p->opts.background_color;
GLSLF("vec4 background = vec4(%f, %f, %f, %f);\n",
c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0);
- GLSL(color.rgb += background.rgb * (1.0 - color.a);)
- GLSL(color.a = background.a;)
+ GLSL(color += background * (1.0 - color.a);)
+ GLSL(color.rgb *= vec3(color.a););
}
}
pass_opt_hook_point(p, "OUTPUT", NULL);
if (flags & RENDER_SCREEN_COLOR)
- pass_dither(p);
+ pass_dither(p, fbo);
pass_describe(p, "output to screen");
finish_pass_fbo(p, fbo, false, &p->dst_rect);
}
@@ -3122,7 +3149,7 @@ static bool update_surface(struct gl_video *p, struct mp_image *mpi,
// because mixing in compressed light artificially darkens the results
if (!p->use_linear) {
p->use_linear = true;
- pass_linearize(p->sc, p->image_params.color.gamma);
+ pass_linearize(p->sc, p->image_params.color.transfer);
}
finish_pass_tex(p, &surf->tex, vp_w, vp_h);
@@ -3134,7 +3161,7 @@ static bool update_surface(struct gl_video *p, struct mp_image *mpi,
// Draws an interpolate frame to fbo, based on the frame timing in t
// flags: bit set of RENDER_FRAME_* flags
static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
- struct ra_fbo fbo, int flags)
+ const struct ra_fbo *fbo, int flags)
{
bool is_new = false;
@@ -3201,7 +3228,7 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
struct mp_image *f = t->frames[i];
uint64_t f_id = t->frame_id + i;
- if (!mp_image_params_equal(&f->params, &p->real_image_params))
+ if (!mp_image_params_static_equal(&f->params, &p->real_image_params))
continue;
if (f_id > p->surfaces[p->surface_idx].id) {
@@ -3306,11 +3333,11 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
}
void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame,
- struct ra_fbo fbo, int flags)
+ const struct ra_fbo *fbo, int flags)
{
gl_video_update_options(p);
- struct mp_rect target_rc = {0, 0, fbo.tex->params.w, fbo.tex->params.h};
+ struct mp_rect target_rc = {0, 0, fbo->tex->params.w, fbo->tex->params.h};
p->broken_frame = false;
@@ -3318,12 +3345,15 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame,
struct m_color c = p->clear_color;
float clear_color[4] = {c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0};
- p->ra->fns->clear(p->ra, fbo.tex, clear_color, &target_rc);
+ clear_color[0] *= clear_color[3];
+ clear_color[1] *= clear_color[3];
+ clear_color[2] *= clear_color[3];
+ p->ra->fns->clear(p->ra, fbo->tex, clear_color, &target_rc);
if (p->hwdec_overlay) {
if (has_frame) {
float *color = p->hwdec_overlay->overlay_colorkey;
- p->ra->fns->clear(p->ra, fbo.tex, color, &p->dst_rect);
+ p->ra->fns->clear(p->ra, fbo->tex, color, &p->dst_rect);
}
p->hwdec_overlay->driver->overlay_frame(p->hwdec_overlay, frame->current,
@@ -3364,43 +3394,41 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame,
// For the non-interpolation case, we draw to a single "cache"
// texture to speed up subsequent re-draws (if any exist)
- struct ra_fbo dest_fbo = fbo;
bool repeats = frame->num_vsyncs > 1 && frame->display_synced;
+ bool r = false;
if ((repeats || frame->still) && !p->dumb_mode &&
- (p->ra->caps & RA_CAP_BLIT) && fbo.tex->params.blit_dst)
+ (p->ra->caps & RA_CAP_BLIT) && fbo->tex->params.blit_dst)
{
// Attempt to use the same format as the destination FBO
// if possible. Some RAs use a wrapped dummy format here,
// so fall back to the fbo_format in that case.
- const struct ra_format *fmt = fbo.tex->params.format;
+ const struct ra_format *fmt = fbo->tex->params.format;
if (fmt->dummy_format)
fmt = p->fbo_format;
-
- bool r = ra_tex_resize(p->ra, p->log, &p->output_tex,
- fbo.tex->params.w, fbo.tex->params.h,
- fmt);
- if (r) {
- dest_fbo = (struct ra_fbo) { p->output_tex };
- p->output_tex_valid = true;
- }
+ r = ra_tex_resize(p->ra, p->log, &p->output_tex,
+ fbo->tex->params.w, fbo->tex->params.h,
+ fmt);
}
+ const struct ra_fbo *dest_fbo = r ? &(struct ra_fbo) { p->output_tex } : fbo;
+ p->output_tex_valid = r;
pass_draw_to_screen(p, dest_fbo, flags);
}
// "output tex valid" and "output tex needed" are equivalent
- if (p->output_tex_valid && fbo.tex->params.blit_dst) {
+ if (p->output_tex_valid && fbo->tex->params.blit_dst) {
pass_info_reset(p, true);
pass_describe(p, "redraw cached frame");
struct mp_rect src = p->dst_rect;
struct mp_rect dst = src;
- if (fbo.flip) {
- dst.y0 = fbo.tex->params.h - src.y0;
- dst.y1 = fbo.tex->params.h - src.y1;
+ if (fbo->flip) {
+ dst.y0 = fbo->tex->params.h - src.y0;
+ dst.y1 = fbo->tex->params.h - src.y1;
}
timer_pool_start(p->blit_timer);
- p->ra->fns->blit(p->ra, fbo.tex, p->output_tex, &dst, &src);
+ p->ra->fns->blit(p->ra, fbo->tex, p->output_tex, &dst, &src);
timer_pool_stop(p->blit_timer);
- pass_record(p, timer_pool_measure(p->blit_timer));
+ struct mp_pass_perf perf = timer_pool_measure(p->blit_timer);
+ pass_record(p, &perf);
}
}
}
@@ -3431,7 +3459,7 @@ done:
// Make the screen solid blue to make it visually clear that an
// error has occurred
float color[4] = {0.0, 0.05, 0.5, 1.0};
- p->ra->fns->clear(p->ra, fbo.tex, color, &target_rc);
+ p->ra->fns->clear(p->ra, fbo->tex, color, &target_rc);
}
p->frames_rendered++;
@@ -3522,7 +3550,7 @@ void gl_video_screenshot(struct gl_video *p, struct vo_frame *frame,
flags |= RENDER_FRAME_OSD;
if (args->scaled)
flags |= RENDER_SCREEN_COLOR;
- gl_video_render_frame(p, nframe, (struct ra_fbo){target}, flags);
+ gl_video_render_frame(p, nframe, &(struct ra_fbo){target}, flags);
res = mp_image_alloc(mpfmt, params.w, params.h);
if (!res)
@@ -3636,7 +3664,8 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t
timer_pool_start(p->upload_timer);
bool ok = ra_hwdec_mapper_map(p->hwdec_mapper, vimg->mpi) >= 0;
timer_pool_stop(p->upload_timer);
- pass_record(p, timer_pool_measure(p->upload_timer));
+ struct mp_pass_perf perf = timer_pool_measure(p->upload_timer);
+ pass_record(p, &perf);
vimg->hwdec_mapped = true;
if (ok) {
@@ -3708,7 +3737,8 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t
bool using_pbo = p->ra->use_pbo || !(p->ra->caps & RA_CAP_DIRECT_UPLOAD);
const char *mode = p->using_dr_path ? "DR" : using_pbo ? "PBO" : "naive";
pass_describe(p, "upload frame (%s)", mode);
- pass_record(p, timer_pool_measure(p->upload_timer));
+ struct mp_pass_perf perf = timer_pool_measure(p->upload_timer);
+ pass_record(p, &perf);
return true;
@@ -3806,9 +3836,8 @@ static void check_gl_features(struct gl_video *p)
p->opts.dither_algo = DITHER_NONE;
MP_WARN(p, "Disabling dithering (no gl_FragCoord).\n");
}
- if (!have_fragcoord && p->opts.alpha_mode == ALPHA_BLEND_TILES) {
- p->opts.alpha_mode = ALPHA_BLEND;
- // Verbose, since this is the default setting
+ if (!have_fragcoord && p->opts.background == BACKGROUND_TILES) {
+ p->opts.background = BACKGROUND_COLOR;
MP_VERBOSE(p, "Disabling alpha checkerboard (no gl_FragCoord).\n");
}
if (!have_fbo && have_compute) {
@@ -3864,9 +3893,9 @@ static void check_gl_features(struct gl_video *p)
.gamma_auto = p->opts.gamma_auto,
.pbo = p->opts.pbo,
.fbo_format = p->opts.fbo_format,
- .alpha_mode = p->opts.alpha_mode,
- .use_rectangle = p->opts.use_rectangle,
.background = p->opts.background,
+ .use_rectangle = p->opts.use_rectangle,
+ .background_color = p->opts.background_color,
.dither_algo = p->opts.dither_algo,
.dither_depth = p->opts.dither_depth,
.dither_size = p->opts.dither_size,
@@ -3913,8 +3942,8 @@ static void check_gl_features(struct gl_video *p)
}
}
- int use_cms = p->opts.target_prim != MP_CSP_PRIM_AUTO ||
- p->opts.target_trc != MP_CSP_TRC_AUTO || p->use_lut_3d;
+ int use_cms = p->opts.target_prim != PL_COLOR_PRIM_UNKNOWN ||
+ p->opts.target_trc != PL_COLOR_TRC_UNKNOWN || p->use_lut_3d;
// mix() is needed for some gamma functions
if (!have_mglsl && (p->opts.linear_downscaling ||
@@ -3926,8 +3955,8 @@ static void check_gl_features(struct gl_video *p)
MP_WARN(p, "Disabling linear/sigmoid scaling (GLSL version too old).\n");
}
if (!have_mglsl && use_cms) {
- p->opts.target_prim = MP_CSP_PRIM_AUTO;
- p->opts.target_trc = MP_CSP_TRC_AUTO;
+ p->opts.target_prim = PL_COLOR_PRIM_UNKNOWN;
+ p->opts.target_trc = PL_COLOR_TRC_UNKNOWN;
p->use_lut_3d = false;
MP_WARN(p, "Disabling color management (GLSL version too old).\n");
}
@@ -4022,7 +4051,7 @@ void gl_video_config(struct gl_video *p, struct mp_image_params *params)
unmap_overlay(p);
unref_current_image(p);
- if (!mp_image_params_equal(&p->real_image_params, params)) {
+ if (!mp_image_params_static_equal(&p->real_image_params, params)) {
uninit_video(p);
p->real_image_params = *params;
p->image_params = *params;
@@ -4117,7 +4146,7 @@ static void reinit_from_options(struct gl_video *p)
p->opts = *(struct gl_video_opts *)p->opts_cache->opts;
if (!p->force_clear_color)
- p->clear_color = p->opts.background;
+ p->clear_color = p->opts.background_color;
check_gl_features(p);
uninit_rendering(p);
@@ -4362,3 +4391,8 @@ void gl_video_load_hwdecs_for_img_fmt(struct gl_video *p, struct mp_hwdec_device
assert(p->hwdec_ctx.ra_ctx);
ra_hwdec_ctx_load_fmt(&p->hwdec_ctx, devs, params);
}
+
+struct mp_image_params *gl_video_get_target_params_ptr(struct gl_video *p)
+{
+ return &p->target_params;
+}
diff --git a/video/out/gpu/video.h b/video/out/gpu/video.h
index 411d336..66ccd9c 100644
--- a/video/out/gpu/video.h
+++ b/video/out/gpu/video.h
@@ -72,11 +72,10 @@ enum dither_algo {
DITHER_ERROR_DIFFUSION,
};
-enum alpha_mode {
- ALPHA_NO = 0,
- ALPHA_YES,
- ALPHA_BLEND,
- ALPHA_BLEND_TILES,
+enum background_type {
+ BACKGROUND_NONE = 0,
+ BACKGROUND_COLOR,
+ BACKGROUND_TILES,
};
enum blend_subs_mode {
@@ -155,9 +154,9 @@ struct gl_video_opts {
int temporal_dither_period;
char *error_diffusion;
char *fbo_format;
- int alpha_mode;
+ int background;
bool use_rectangle;
- struct m_color background;
+ struct m_color background_color;
bool interpolation;
float interpolation_threshold;
int blend_subs;
@@ -195,7 +194,7 @@ void gl_video_set_osd_source(struct gl_video *p, struct osd_state *osd);
bool gl_video_check_format(struct gl_video *p, int mp_format);
void gl_video_config(struct gl_video *p, struct mp_image_params *params);
void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame,
- struct ra_fbo fbo, int flags);
+ const struct ra_fbo *fbo, int flags);
void gl_video_resize(struct gl_video *p,
struct mp_rect *src, struct mp_rect *dst,
struct mp_osd_res *osd);
@@ -215,7 +214,6 @@ void gl_video_set_ambient_lux(struct gl_video *p, int lux);
void gl_video_set_icc_profile(struct gl_video *p, bstr icc_data);
bool gl_video_icc_auto_enabled(struct gl_video *p);
bool gl_video_gamma_auto_enabled(struct gl_video *p);
-struct mp_colorspace gl_video_get_output_colorspace(struct gl_video *p);
void gl_video_reset(struct gl_video *p);
bool gl_video_showing_interpolated_frame(struct gl_video *p);
@@ -234,5 +232,6 @@ void gl_video_configure_queue(struct gl_video *p, struct vo *vo);
struct mp_image *gl_video_get_image(struct gl_video *p, int imgfmt, int w, int h,
int stride_align, int flags);
+struct mp_image_params *gl_video_get_target_params_ptr(struct gl_video *p);
#endif
diff --git a/video/out/gpu/video_shaders.c b/video/out/gpu/video_shaders.c
index 6c0e8a8..e202818 100644
--- a/video/out/gpu/video_shaders.c
+++ b/video/out/gpu/video_shaders.c
@@ -17,6 +17,8 @@
#include <math.h>
+#include <libplacebo/colorspace.h>
+
#include "video_shaders.h"
#include "video.h"
@@ -252,7 +254,7 @@ void pass_compute_polar(struct gl_shader_cache *sc, struct scaler *scaler,
static void bicubic_calcweights(struct gl_shader_cache *sc, const char *t, const char *s)
{
// Explanation of how bicubic scaling with only 4 texel fetches is done:
- // http://www.mate.tue.nl/mate/pdfs/10318.pdf
+ // <https://web.archive.org/web/20180720154854/http://www.mate.tue.nl/mate/pdfs/10318.pdf>
// 'Efficient GPU-Based Texture Interpolation using Uniform B-Splines'
// Explanation why this algorithm normally always blurs, even with unit
// scaling:
@@ -337,10 +339,10 @@ static const float SLOG_A = 0.432699,
//
// These functions always output to a normalized scale of [0,1], for
// convenience of the video.c code that calls it. To get the values in an
-// absolute scale, multiply the result by `mp_trc_nom_peak(trc)`
-void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
+// absolute scale, multiply the result by `pl_color_transfer_nominal_peak(trc)`
+void pass_linearize(struct gl_shader_cache *sc, enum pl_color_transfer trc)
{
- if (trc == MP_CSP_TRC_LINEAR)
+ if (trc == PL_COLOR_TRC_LINEAR)
return;
GLSLF("// linearize\n");
@@ -353,40 +355,40 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
switch (trc) {
- case MP_CSP_TRC_SRGB:
+ case PL_COLOR_TRC_SRGB:
GLSLF("color.rgb = mix(color.rgb * vec3(1.0/12.92), \n"
" pow((color.rgb + vec3(0.055))/vec3(1.055), vec3(2.4)), \n"
" %s(lessThan(vec3(0.04045), color.rgb))); \n",
gl_sc_bvec(sc, 3));
break;
- case MP_CSP_TRC_BT_1886:
+ case PL_COLOR_TRC_BT_1886:
GLSL(color.rgb = pow(color.rgb, vec3(2.4));)
break;
- case MP_CSP_TRC_GAMMA18:
+ case PL_COLOR_TRC_GAMMA18:
GLSL(color.rgb = pow(color.rgb, vec3(1.8));)
break;
- case MP_CSP_TRC_GAMMA20:
+ case PL_COLOR_TRC_GAMMA20:
GLSL(color.rgb = pow(color.rgb, vec3(2.0));)
break;
- case MP_CSP_TRC_GAMMA22:
+ case PL_COLOR_TRC_GAMMA22:
GLSL(color.rgb = pow(color.rgb, vec3(2.2));)
break;
- case MP_CSP_TRC_GAMMA24:
+ case PL_COLOR_TRC_GAMMA24:
GLSL(color.rgb = pow(color.rgb, vec3(2.4));)
break;
- case MP_CSP_TRC_GAMMA26:
+ case PL_COLOR_TRC_GAMMA26:
GLSL(color.rgb = pow(color.rgb, vec3(2.6));)
break;
- case MP_CSP_TRC_GAMMA28:
+ case PL_COLOR_TRC_GAMMA28:
GLSL(color.rgb = pow(color.rgb, vec3(2.8));)
break;
- case MP_CSP_TRC_PRO_PHOTO:
+ case PL_COLOR_TRC_PRO_PHOTO:
GLSLF("color.rgb = mix(color.rgb * vec3(1.0/16.0), \n"
" pow(color.rgb, vec3(1.8)), \n"
" %s(lessThan(vec3(0.03125), color.rgb))); \n",
gl_sc_bvec(sc, 3));
break;
- case MP_CSP_TRC_PQ:
+ case PL_COLOR_TRC_PQ:
GLSLF("color.rgb = pow(color.rgb, vec3(1.0/%f));\n", PQ_M2);
GLSLF("color.rgb = max(color.rgb - vec3(%f), vec3(0.0)) \n"
" / (vec3(%f) - vec3(%f) * color.rgb);\n",
@@ -396,33 +398,33 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
// MP_REF_WHITE instead, so rescale
GLSLF("color.rgb *= vec3(%f);\n", 10000 / MP_REF_WHITE);
break;
- case MP_CSP_TRC_HLG:
+ case PL_COLOR_TRC_HLG:
GLSLF("color.rgb = mix(vec3(4.0) * color.rgb * color.rgb,\n"
" exp((color.rgb - vec3(%f)) * vec3(1.0/%f)) + vec3(%f),\n"
" %s(lessThan(vec3(0.5), color.rgb)));\n",
HLG_C, HLG_A, HLG_B, gl_sc_bvec(sc, 3));
GLSLF("color.rgb *= vec3(1.0/%f);\n", MP_REF_WHITE_HLG);
break;
- case MP_CSP_TRC_V_LOG:
+ case PL_COLOR_TRC_V_LOG:
GLSLF("color.rgb = mix((color.rgb - vec3(0.125)) * vec3(1.0/5.6), \n"
" pow(vec3(10.0), (color.rgb - vec3(%f)) * vec3(1.0/%f)) \n"
" - vec3(%f), \n"
" %s(lessThanEqual(vec3(0.181), color.rgb))); \n",
VLOG_D, VLOG_C, VLOG_B, gl_sc_bvec(sc, 3));
break;
- case MP_CSP_TRC_S_LOG1:
+ case PL_COLOR_TRC_S_LOG1:
GLSLF("color.rgb = pow(vec3(10.0), (color.rgb - vec3(%f)) * vec3(1.0/%f))\n"
" - vec3(%f);\n",
SLOG_C, SLOG_A, SLOG_B);
break;
- case MP_CSP_TRC_S_LOG2:
+ case PL_COLOR_TRC_S_LOG2:
GLSLF("color.rgb = mix((color.rgb - vec3(%f)) * vec3(1.0/%f), \n"
" (pow(vec3(10.0), (color.rgb - vec3(%f)) * vec3(1.0/%f)) \n"
" - vec3(%f)) * vec3(1.0/%f), \n"
" %s(lessThanEqual(vec3(%f), color.rgb))); \n",
SLOG_Q, SLOG_P, SLOG_C, SLOG_A, SLOG_B, SLOG_K2, gl_sc_bvec(sc, 3), SLOG_Q);
break;
- case MP_CSP_TRC_ST428:
+ case PL_COLOR_TRC_ST428:
GLSL(color.rgb = vec3(52.37/48.0) * pow(color.rgb, vec3(2.6)););
break;
default:
@@ -430,7 +432,7 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
}
// Rescale to prevent clipping on non-float textures
- GLSLF("color.rgb *= vec3(1.0/%f);\n", mp_trc_nom_peak(trc));
+ GLSLF("color.rgb *= vec3(1.0/%f);\n", pl_color_transfer_nominal_peak(trc));
}
// Delinearize (compress), given a TRC as output. This corresponds to the
@@ -438,51 +440,51 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
// reference monitor.
//
// Like pass_linearize, this functions ingests values on an normalized scale
-void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
+void pass_delinearize(struct gl_shader_cache *sc, enum pl_color_transfer trc)
{
- if (trc == MP_CSP_TRC_LINEAR)
+ if (trc == PL_COLOR_TRC_LINEAR)
return;
GLSLF("// delinearize\n");
GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
- GLSLF("color.rgb *= vec3(%f);\n", mp_trc_nom_peak(trc));
+ GLSLF("color.rgb *= vec3(%f);\n", pl_color_transfer_nominal_peak(trc));
switch (trc) {
- case MP_CSP_TRC_SRGB:
+ case PL_COLOR_TRC_SRGB:
GLSLF("color.rgb = mix(color.rgb * vec3(12.92), \n"
" vec3(1.055) * pow(color.rgb, vec3(1.0/2.4)) \n"
" - vec3(0.055), \n"
" %s(lessThanEqual(vec3(0.0031308), color.rgb))); \n",
gl_sc_bvec(sc, 3));
break;
- case MP_CSP_TRC_BT_1886:
+ case PL_COLOR_TRC_BT_1886:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.4));)
break;
- case MP_CSP_TRC_GAMMA18:
+ case PL_COLOR_TRC_GAMMA18:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/1.8));)
break;
- case MP_CSP_TRC_GAMMA20:
+ case PL_COLOR_TRC_GAMMA20:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.0));)
break;
- case MP_CSP_TRC_GAMMA22:
+ case PL_COLOR_TRC_GAMMA22:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.2));)
break;
- case MP_CSP_TRC_GAMMA24:
+ case PL_COLOR_TRC_GAMMA24:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.4));)
break;
- case MP_CSP_TRC_GAMMA26:
+ case PL_COLOR_TRC_GAMMA26:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.6));)
break;
- case MP_CSP_TRC_GAMMA28:
+ case PL_COLOR_TRC_GAMMA28:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.8));)
break;
- case MP_CSP_TRC_PRO_PHOTO:
+ case PL_COLOR_TRC_PRO_PHOTO:
GLSLF("color.rgb = mix(color.rgb * vec3(16.0), \n"
" pow(color.rgb, vec3(1.0/1.8)), \n"
" %s(lessThanEqual(vec3(0.001953), color.rgb))); \n",
gl_sc_bvec(sc, 3));
break;
- case MP_CSP_TRC_PQ:
+ case PL_COLOR_TRC_PQ:
GLSLF("color.rgb *= vec3(1.0/%f);\n", 10000 / MP_REF_WHITE);
GLSLF("color.rgb = pow(color.rgb, vec3(%f));\n", PQ_M1);
GLSLF("color.rgb = (vec3(%f) + vec3(%f) * color.rgb) \n"
@@ -490,32 +492,32 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
PQ_C1, PQ_C2, PQ_C3);
GLSLF("color.rgb = pow(color.rgb, vec3(%f));\n", PQ_M2);
break;
- case MP_CSP_TRC_HLG:
+ case PL_COLOR_TRC_HLG:
GLSLF("color.rgb *= vec3(%f);\n", MP_REF_WHITE_HLG);
GLSLF("color.rgb = mix(vec3(0.5) * sqrt(color.rgb),\n"
" vec3(%f) * log(color.rgb - vec3(%f)) + vec3(%f),\n"
" %s(lessThan(vec3(1.0), color.rgb)));\n",
HLG_A, HLG_B, HLG_C, gl_sc_bvec(sc, 3));
break;
- case MP_CSP_TRC_V_LOG:
+ case PL_COLOR_TRC_V_LOG:
GLSLF("color.rgb = mix(vec3(5.6) * color.rgb + vec3(0.125), \n"
" vec3(%f) * log(color.rgb + vec3(%f)) \n"
" + vec3(%f), \n"
" %s(lessThanEqual(vec3(0.01), color.rgb))); \n",
VLOG_C / M_LN10, VLOG_B, VLOG_D, gl_sc_bvec(sc, 3));
break;
- case MP_CSP_TRC_S_LOG1:
+ case PL_COLOR_TRC_S_LOG1:
GLSLF("color.rgb = vec3(%f) * log(color.rgb + vec3(%f)) + vec3(%f);\n",
SLOG_A / M_LN10, SLOG_B, SLOG_C);
break;
- case MP_CSP_TRC_S_LOG2:
+ case PL_COLOR_TRC_S_LOG2:
GLSLF("color.rgb = mix(vec3(%f) * color.rgb + vec3(%f), \n"
" vec3(%f) * log(vec3(%f) * color.rgb + vec3(%f)) \n"
" + vec3(%f), \n"
" %s(lessThanEqual(vec3(0.0), color.rgb))); \n",
SLOG_P, SLOG_Q, SLOG_A / M_LN10, SLOG_K2, SLOG_B, SLOG_C, gl_sc_bvec(sc, 3));
break;
- case MP_CSP_TRC_ST428:
+ case PL_COLOR_TRC_ST428:
GLSL(color.rgb = pow(color.rgb * vec3(48.0/52.37), vec3(1.0/2.6)););
break;
default:
@@ -834,42 +836,42 @@ static void pass_tone_map(struct gl_shader_cache *sc,
// the caller to have already bound the appropriate SSBO and set up the compute
// shader metadata
void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
- struct mp_colorspace src, struct mp_colorspace dst,
+ struct pl_color_space src, struct pl_color_space dst,
+ enum mp_csp_light src_light, enum mp_csp_light dst_light,
const struct gl_tone_map_opts *opts)
{
GLSLF("// color mapping\n");
// Some operations need access to the video's luma coefficients, so make
// them available
- float rgb2xyz[3][3];
- mp_get_rgb2xyz_matrix(mp_get_csp_primaries(src.primaries), rgb2xyz);
- gl_sc_uniform_vec3(sc, "src_luma", rgb2xyz[1]);
- mp_get_rgb2xyz_matrix(mp_get_csp_primaries(dst.primaries), rgb2xyz);
- gl_sc_uniform_vec3(sc, "dst_luma", rgb2xyz[1]);
-
- bool need_ootf = src.light != dst.light;
- if (src.light == MP_CSP_LIGHT_SCENE_HLG && src.hdr.max_luma != dst.hdr.max_luma)
+ pl_matrix3x3 rgb2xyz = pl_get_rgb2xyz_matrix(pl_raw_primaries_get(src.primaries));
+ gl_sc_uniform_vec3(sc, "src_luma", rgb2xyz.m[1]);
+ rgb2xyz = pl_get_rgb2xyz_matrix(pl_raw_primaries_get(dst.primaries));
+ gl_sc_uniform_vec3(sc, "dst_luma", rgb2xyz.m[1]);
+
+ bool need_ootf = src_light != dst_light;
+ if (src_light == MP_CSP_LIGHT_SCENE_HLG && src.hdr.max_luma != dst.hdr.max_luma)
need_ootf = true;
// All operations from here on require linear light as a starting point,
- // so we linearize even if src.gamma == dst.gamma when one of the other
+ // so we linearize even if src.gamma == dst.transfer when one of the other
// operations needs it
- bool need_linear = src.gamma != dst.gamma ||
+ bool need_linear = src.transfer != dst.transfer ||
src.primaries != dst.primaries ||
src.hdr.max_luma != dst.hdr.max_luma ||
need_ootf;
if (need_linear && !is_linear) {
// We also pull it up so that 1.0 is the reference white
- pass_linearize(sc, src.gamma);
+ pass_linearize(sc, src.transfer);
is_linear = true;
}
// Pre-scale the incoming values into an absolute scale
- GLSLF("color.rgb *= vec3(%f);\n", mp_trc_nom_peak(src.gamma));
+ GLSLF("color.rgb *= vec3(%f);\n", pl_color_transfer_nominal_peak(src.transfer));
if (need_ootf)
- pass_ootf(sc, src.light, src.hdr.max_luma / MP_REF_WHITE);
+ pass_ootf(sc, src_light, src.hdr.max_luma / MP_REF_WHITE);
// Tone map to prevent clipping due to excessive brightness
if (src.hdr.max_luma > dst.hdr.max_luma) {
@@ -879,11 +881,11 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
// Adapt to the right colorspace if necessary
if (src.primaries != dst.primaries) {
- struct mp_csp_primaries csp_src = mp_get_csp_primaries(src.primaries),
- csp_dst = mp_get_csp_primaries(dst.primaries);
- float m[3][3] = {{0}};
- mp_get_cms_matrix(csp_src, csp_dst, MP_INTENT_RELATIVE_COLORIMETRIC, m);
- gl_sc_uniform_mat3(sc, "cms_matrix", true, &m[0][0]);
+ const struct pl_raw_primaries *csp_src = pl_raw_primaries_get(src.primaries),
+ *csp_dst = pl_raw_primaries_get(dst.primaries);
+ pl_matrix3x3 m = pl_get_color_mapping_matrix(csp_src, csp_dst,
+ PL_INTENT_RELATIVE_COLORIMETRIC);
+ gl_sc_uniform_mat3(sc, "cms_matrix", true, &m.m[0][0]);
GLSL(color.rgb = cms_matrix * color.rgb;)
if (!opts->gamut_mode || opts->gamut_mode == GAMUT_DESATURATE) {
@@ -900,14 +902,14 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
}
if (need_ootf)
- pass_inverse_ootf(sc, dst.light, dst.hdr.max_luma / MP_REF_WHITE);
+ pass_inverse_ootf(sc, dst_light, dst.hdr.max_luma / MP_REF_WHITE);
// Post-scale the outgoing values from absolute scale to normalized.
// For SDR, we normalize to the chosen signal peak. For HDR, we normalize
// to the encoding range of the transfer function.
float dst_range = dst.hdr.max_luma / MP_REF_WHITE;
- if (mp_trc_is_hdr(dst.gamma))
- dst_range = mp_trc_nom_peak(dst.gamma);
+ if (pl_color_space_is_hdr(&dst))
+ dst_range = pl_color_transfer_nominal_peak(dst.transfer);
GLSLF("color.rgb *= vec3(%f);\n", 1.0 / dst_range);
@@ -919,7 +921,7 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
}
if (is_linear)
- pass_delinearize(sc, dst.gamma);
+ pass_delinearize(sc, dst.transfer);
}
// Wide usage friendly PRNG, shamelessly stolen from a GLSL tricks forum post.
@@ -964,7 +966,7 @@ const struct m_sub_options deband_conf = {
// Stochastically sample a debanded result from a hooked texture.
void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
- AVLFG *lfg, enum mp_csp_trc trc)
+ AVLFG *lfg, enum pl_color_transfer trc)
{
// Initialize the PRNG
GLSLF("{\n");
@@ -1008,7 +1010,7 @@ void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
GLSL(noise.z = rand(h); h = permute(h);)
// Noise is scaled to the signal level to prevent extreme noise for HDR
- float gain = opts->grain/8192.0 / mp_trc_nom_peak(trc);
+ float gain = opts->grain/8192.0 / pl_color_transfer_nominal_peak(trc);
GLSLF("color.xyz += %f * (noise - vec3(0.5));\n", gain);
GLSLF("}\n");
}
diff --git a/video/out/gpu/video_shaders.h b/video/out/gpu/video_shaders.h
index 27e7874..7547df6 100644
--- a/video/out/gpu/video_shaders.h
+++ b/video/out/gpu/video_shaders.h
@@ -44,15 +44,16 @@ void pass_sample_bicubic_fast(struct gl_shader_cache *sc);
void pass_sample_oversample(struct gl_shader_cache *sc, struct scaler *scaler,
int w, int h);
-void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc);
-void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc);
+void pass_linearize(struct gl_shader_cache *sc, enum pl_color_transfer trc);
+void pass_delinearize(struct gl_shader_cache *sc, enum pl_color_transfer trc);
void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
- struct mp_colorspace src, struct mp_colorspace dst,
+ struct pl_color_space src, struct pl_color_space dst,
+ enum mp_csp_light src_light, enum mp_csp_light dst_light,
const struct gl_tone_map_opts *opts);
void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
- AVLFG *lfg, enum mp_csp_trc trc);
+ AVLFG *lfg, enum pl_color_transfer trc);
void pass_sample_unsharp(struct gl_shader_cache *sc, float param);
diff --git a/video/out/gpu_next/context.c b/video/out/gpu_next/context.c
index 2887cff..2c7c9fa 100644
--- a/video/out/gpu_next/context.c
+++ b/video/out/gpu_next/context.c
@@ -88,6 +88,7 @@ static bool d3d11_pl_init(struct vo *vo, struct gpu_ctx *ctx,
ctx->swapchain = pl_d3d11_create_swapchain(d3d11,
pl_d3d11_swapchain_params(
.swapchain = swapchain,
+ .disable_10bit_sdr = ra_d3d11_ctx_prefer_8bit_output_format(ctx->ra_ctx),
)
);
if (!ctx->swapchain) {
@@ -106,13 +107,10 @@ err_out:
}
#endif // HAVE_D3D11
-struct gpu_ctx *gpu_ctx_create(struct vo *vo, struct gl_video_opts *gl_opts)
+struct gpu_ctx *gpu_ctx_create(struct vo *vo, struct ra_ctx_opts *ctx_opts)
{
struct gpu_ctx *ctx = talloc_zero(NULL, struct gpu_ctx);
ctx->log = vo->log;
-
- struct ra_ctx_opts *ctx_opts = mp_get_config_group(ctx, vo->global, &ra_ctx_conf);
- ctx_opts->want_alpha = gl_opts->alpha_mode == ALPHA_YES;
ctx->ra_ctx = ra_ctx_create(vo, *ctx_opts);
if (!ctx->ra_ctx)
goto err_out;
@@ -145,18 +143,17 @@ struct gpu_ctx *gpu_ctx_create(struct vo *vo, struct gl_video_opts *gl_opts)
#if HAVE_GL && defined(PL_HAVE_OPENGL)
if (ra_is_gl(ctx->ra_ctx->ra)) {
struct GL *gl = ra_gl_get(ctx->ra_ctx->ra);
- pl_opengl opengl = pl_opengl_create(ctx->pllog,
- pl_opengl_params(
- .debug = ctx_opts->debug,
- .allow_software = ctx_opts->allow_sw,
- .get_proc_addr_ex = (void *) gl->get_fn,
- .proc_ctx = gl->fn_ctx,
+ struct pl_opengl_params params = *pl_opengl_params(
+ .debug = ctx_opts->debug,
+ .allow_software = ctx_opts->allow_sw,
+ .get_proc_addr_ex = (void *) gl->get_fn,
+ .proc_ctx = gl->fn_ctx,
+ );
# if HAVE_EGL
- .egl_display = eglGetCurrentDisplay(),
- .egl_context = eglGetCurrentContext(),
+ params.egl_display = eglGetCurrentDisplay();
+ params.egl_context = eglGetCurrentContext();
# endif
- )
- );
+ pl_opengl opengl = pl_opengl_create(ctx->pllog, &params);
if (!opengl)
goto err_out;
ctx->gpu = opengl->gpu;
diff --git a/video/out/gpu_next/context.h b/video/out/gpu_next/context.h
index b98b9e7..aa44196 100644
--- a/video/out/gpu_next/context.h
+++ b/video/out/gpu_next/context.h
@@ -21,8 +21,8 @@
struct mp_log;
struct ra_ctx;
+struct ra_ctx_opts;
struct vo;
-struct gl_video_opts;
struct gpu_ctx {
struct mp_log *log;
@@ -35,6 +35,6 @@ struct gpu_ctx {
void *priv;
};
-struct gpu_ctx *gpu_ctx_create(struct vo *vo, struct gl_video_opts *gl_opts);
+struct gpu_ctx *gpu_ctx_create(struct vo *vo, struct ra_ctx_opts *ctx_opts);
bool gpu_ctx_resize(struct gpu_ctx *ctx, int w, int h);
void gpu_ctx_destroy(struct gpu_ctx **ctxp);
diff --git a/video/out/hwdec/dmabuf_interop.h b/video/out/hwdec/dmabuf_interop.h
index e9b3e8e..3bf01a0 100644
--- a/video/out/hwdec/dmabuf_interop.h
+++ b/video/out/hwdec/dmabuf_interop.h
@@ -38,7 +38,7 @@ struct dmabuf_interop {
struct dmabuf_interop_priv {
int num_planes;
struct mp_image layout;
- struct ra_tex *tex[4];
+ struct ra_tex *tex[AV_DRM_MAX_PLANES];
AVDRMFrameDescriptor desc;
bool surface_acquired;
diff --git a/video/out/hwdec/dmabuf_interop_gl.c b/video/out/hwdec/dmabuf_interop_gl.c
index e7fb103..0f6fb89 100644
--- a/video/out/hwdec/dmabuf_interop_gl.c
+++ b/video/out/hwdec/dmabuf_interop_gl.c
@@ -52,39 +52,28 @@ typedef void *EGLImageKHR;
#define EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT 0x344A
struct vaapi_gl_mapper_priv {
- GLuint gl_textures[4];
- EGLImageKHR images[4];
+ GLuint gl_textures[AV_DRM_MAX_PLANES];
+ EGLImageKHR images[AV_DRM_MAX_PLANES];
+
+ const struct ra_format *planes[AV_DRM_MAX_PLANES];
EGLImageKHR (EGLAPIENTRY *CreateImageKHR)(EGLDisplay, EGLContext,
EGLenum, EGLClientBuffer,
const EGLint *);
EGLBoolean (EGLAPIENTRY *DestroyImageKHR)(EGLDisplay, EGLImageKHR);
void (EGLAPIENTRY *EGLImageTargetTexture2DOES)(GLenum, GLeglImageOES);
+ void (EGLAPIENTRY *EGLImageTargetTexStorageEXT)(GLenum, GLeglImageOES,
+ const GLint *);
};
-static bool vaapi_gl_mapper_init(struct ra_hwdec_mapper *mapper,
- const struct ra_imgfmt_desc *desc)
+static bool gl_create_textures(struct ra_hwdec_mapper *mapper)
{
struct dmabuf_interop_priv *p_mapper = mapper->priv;
- struct vaapi_gl_mapper_priv *p = talloc_ptrtype(NULL, p);
- p_mapper->interop_mapper_priv = p;
-
- *p = (struct vaapi_gl_mapper_priv) {
- // EGL_KHR_image_base
- .CreateImageKHR = (void *)eglGetProcAddress("eglCreateImageKHR"),
- .DestroyImageKHR = (void *)eglGetProcAddress("eglDestroyImageKHR"),
- // GL_OES_EGL_image
- .EGLImageTargetTexture2DOES =
- (void *)eglGetProcAddress("glEGLImageTargetTexture2DOES"),
- };
-
- if (!p->CreateImageKHR || !p->DestroyImageKHR ||
- !p->EGLImageTargetTexture2DOES)
- return false;
+ struct vaapi_gl_mapper_priv *p = p_mapper->interop_mapper_priv;
GL *gl = ra_gl_get(mapper->ra);
- gl->GenTextures(4, p->gl_textures);
- for (int n = 0; n < desc->num_planes; n++) {
+ gl->GenTextures(AV_DRM_MAX_PLANES, p->gl_textures);
+ for (int n = 0; n < p_mapper->num_planes; n++) {
gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -97,7 +86,7 @@ static bool vaapi_gl_mapper_init(struct ra_hwdec_mapper *mapper,
.w = mp_image_plane_w(&p_mapper->layout, n),
.h = mp_image_plane_h(&p_mapper->layout, n),
.d = 1,
- .format = desc->planes[n],
+ .format = p->planes[n],
.render_src = true,
.src_linear = true,
};
@@ -114,44 +103,99 @@ static bool vaapi_gl_mapper_init(struct ra_hwdec_mapper *mapper,
return true;
}
+static void gl_delete_textures(const struct ra_hwdec_mapper *mapper)
+{
+ struct dmabuf_interop_priv *p_mapper = mapper->priv;
+ struct vaapi_gl_mapper_priv *p = p_mapper->interop_mapper_priv;
+
+ GL *gl = ra_gl_get(mapper->ra);
+ gl->DeleteTextures(AV_DRM_MAX_PLANES, p->gl_textures);
+ for (int n = 0; n < AV_DRM_MAX_PLANES; n++) {
+ p->gl_textures[n] = 0;
+ ra_tex_free(mapper->ra, &p_mapper->tex[n]);
+ }
+}
+
+static bool vaapi_gl_mapper_init(struct ra_hwdec_mapper *mapper,
+ const struct ra_imgfmt_desc *desc)
+{
+ struct dmabuf_interop_priv *p_mapper = mapper->priv;
+ struct vaapi_gl_mapper_priv *p = talloc_ptrtype(NULL, p);
+ p_mapper->interop_mapper_priv = p;
+
+ *p = (struct vaapi_gl_mapper_priv) {
+ // EGL_KHR_image_base
+ .CreateImageKHR = (void *)eglGetProcAddress("eglCreateImageKHR"),
+ .DestroyImageKHR = (void *)eglGetProcAddress("eglDestroyImageKHR"),
+ };
+ if (ra_gl_get(mapper->ra)->es) {
+ // GL_OES_EGL_image
+ p->EGLImageTargetTexture2DOES =
+ (void *)eglGetProcAddress("glEGLImageTargetTexture2DOES");
+ } else {
+ // GL_EXT_EGL_image_storage
+ p->EGLImageTargetTexStorageEXT =
+ (void *)eglGetProcAddress("glEGLImageTargetTexStorageEXT");
+ }
+
+ if (!p->CreateImageKHR || !p->DestroyImageKHR ||
+ (!p->EGLImageTargetTexture2DOES && !p->EGLImageTargetTexStorageEXT)) {
+ return false;
+ }
+
+ static_assert(MP_ARRAY_SIZE(desc->planes) == AV_DRM_MAX_PLANES, "");
+ static_assert(MP_ARRAY_SIZE(mapper->tex) == AV_DRM_MAX_PLANES, "");
+
+ // remember format to allow texture recreation
+ for (int n = 0; n < desc->num_planes; n++) {
+ p->planes[n] = desc->planes[n];
+ }
+ if (p->EGLImageTargetTexture2DOES) {
+ // created only once
+ if (!gl_create_textures(mapper))
+ return false;
+ }
+
+ return true;
+}
+
static void vaapi_gl_mapper_uninit(const struct ra_hwdec_mapper *mapper)
{
struct dmabuf_interop_priv *p_mapper = mapper->priv;
struct vaapi_gl_mapper_priv *p = p_mapper->interop_mapper_priv;
if (p) {
- GL *gl = ra_gl_get(mapper->ra);
- gl->DeleteTextures(4, p->gl_textures);
- for (int n = 0; n < 4; n++) {
- p->gl_textures[n] = 0;
- ra_tex_free(mapper->ra, &p_mapper->tex[n]);
- }
+ gl_delete_textures(mapper);
talloc_free(p);
p_mapper->interop_mapper_priv = NULL;
}
}
-#define ADD_ATTRIB(name, value) \
- do { \
- assert(num_attribs + 3 < MP_ARRAY_SIZE(attribs)); \
- attribs[num_attribs++] = (name); \
- attribs[num_attribs++] = (value); \
- attribs[num_attribs] = EGL_NONE; \
+#define ADD_ATTRIB(name, value) \
+ do { \
+ assert(num_attribs + 3 < MP_ARRAY_SIZE(attribs)); \
+ attribs[num_attribs++] = (name); \
+ attribs[num_attribs++] = (value); \
+ attribs[num_attribs] = EGL_NONE; \
} while(0)
-#define ADD_PLANE_ATTRIBS(plane) do { \
- uint64_t drm_format_modifier = p_mapper->desc.objects[p_mapper->desc.layers[i].planes[j].object_index].format_modifier; \
- ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _FD_EXT, \
- p_mapper->desc.objects[p_mapper->desc.layers[i].planes[j].object_index].fd); \
- ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _OFFSET_EXT, \
- p_mapper->desc.layers[i].planes[j].offset); \
- ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _PITCH_EXT, \
- p_mapper->desc.layers[i].planes[j].pitch); \
- if (dmabuf_interop->use_modifiers && drm_format_modifier != DRM_FORMAT_MOD_INVALID) { \
- ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _MODIFIER_LO_EXT, drm_format_modifier & 0xfffffffful); \
- ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _MODIFIER_HI_EXT, drm_format_modifier >> 32); \
- } \
- } while (0)
+#define ADD_PLANE_ATTRIBS(nplane) \
+ do { \
+ const AVDRMPlaneDescriptor *plane = &p_mapper->desc.layers[i].planes[j]; \
+ const AVDRMObjectDescriptor *object = \
+ &p_mapper->desc.objects[plane->object_index]; \
+ ADD_ATTRIB(EGL_DMA_BUF_PLANE ## nplane ## _FD_EXT, object->fd); \
+ ADD_ATTRIB(EGL_DMA_BUF_PLANE ## nplane ## _OFFSET_EXT, plane->offset); \
+ ADD_ATTRIB(EGL_DMA_BUF_PLANE ## nplane ## _PITCH_EXT, plane->pitch); \
+ uint64_t drm_format_modifier = object->format_modifier; \
+ if (dmabuf_interop->use_modifiers && \
+ drm_format_modifier != DRM_FORMAT_MOD_INVALID) { \
+ ADD_ATTRIB(EGL_DMA_BUF_PLANE ## nplane ## _MODIFIER_LO_EXT, \
+ drm_format_modifier & 0xfffffffful); \
+ ADD_ATTRIB(EGL_DMA_BUF_PLANE ## nplane ## _MODIFIER_HI_EXT, \
+ drm_format_modifier >> 32); \
+ } \
+ } while (0)
static bool vaapi_gl_map(struct ra_hwdec_mapper *mapper,
struct dmabuf_interop *dmabuf_interop,
@@ -162,6 +206,11 @@ static bool vaapi_gl_map(struct ra_hwdec_mapper *mapper,
GL *gl = ra_gl_get(mapper->ra);
+ if (p->EGLImageTargetTexStorageEXT) {
+ if (!gl_create_textures(mapper))
+ return false;
+ }
+
for (int i = 0, n = 0; i < p_mapper->desc.nb_layers; i++) {
/*
* As we must map surfaces as one texture per plane, we can only support
@@ -186,6 +235,7 @@ static bool vaapi_gl_map(struct ra_hwdec_mapper *mapper,
format[2] = DRM_FORMAT_R8;
break;
case DRM_FORMAT_P010:
+ case DRM_FORMAT_P210:
#ifdef DRM_FORMAT_P030 /* Format added in a newer libdrm version than minimum */
case DRM_FORMAT_P030:
#endif
@@ -251,7 +301,11 @@ static bool vaapi_gl_map(struct ra_hwdec_mapper *mapper,
}
gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]);
- p->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, p->images[n]);
+ if (p->EGLImageTargetTexStorageEXT) {
+ p->EGLImageTargetTexStorageEXT(GL_TEXTURE_2D, p->images[n], NULL);
+ } else {
+ p->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, p->images[n]);
+ }
mapper->tex[n] = p_mapper->tex[n];
}
@@ -266,12 +320,18 @@ static void vaapi_gl_unmap(struct ra_hwdec_mapper *mapper)
struct dmabuf_interop_priv *p_mapper = mapper->priv;
struct vaapi_gl_mapper_priv *p = p_mapper->interop_mapper_priv;
- if (p) {
- for (int n = 0; n < 4; n++) {
- if (p->images[n])
- p->DestroyImageKHR(eglGetCurrentDisplay(), p->images[n]);
- p->images[n] = 0;
- }
+ if (!p)
+ return;
+
+ if (p->EGLImageTargetTexStorageEXT) {
+ // textures are immutable, can't reuse
+ gl_delete_textures(mapper);
+ }
+
+ for (int n = 0; n < AV_DRM_MAX_PLANES; n++) {
+ if (p->images[n])
+ p->DestroyImageKHR(eglGetCurrentDisplay(), p->images[n]);
+ p->images[n] = 0;
}
}
@@ -291,16 +351,18 @@ bool dmabuf_interop_gl_init(const struct ra_hwdec *hw,
return false;
GL *gl = ra_gl_get(hw->ra_ctx->ra);
+ const char *imageext = gl->es ? "GL_OES_EGL_image" : "GL_EXT_EGL_image_storage";
if (!gl_check_extension(exts, "EGL_EXT_image_dma_buf_import") ||
!gl_check_extension(exts, "EGL_KHR_image_base") ||
- !gl_check_extension(gl->extensions, "GL_OES_EGL_image") ||
- !(gl->mpgl_caps & MPGL_CAP_TEX_RG))
+ !gl_check_extension(gl->extensions, imageext) ||
+ !(gl->mpgl_caps & MPGL_CAP_TEX_RG)) {
return false;
+ }
dmabuf_interop->use_modifiers =
gl_check_extension(exts, "EGL_EXT_image_dma_buf_import_modifiers");
- MP_VERBOSE(hw, "using EGL dmabuf interop\n");
+ MP_VERBOSE(hw, "Using EGL dmabuf interop via %s\n", imageext);
dmabuf_interop->interop_init = vaapi_gl_mapper_init;
dmabuf_interop->interop_uninit = vaapi_gl_mapper_uninit;
diff --git a/video/out/hwdec/dmabuf_interop_pl.c b/video/out/hwdec/dmabuf_interop_pl.c
index 0a8ec5b..1f036e3 100644
--- a/video/out/hwdec/dmabuf_interop_pl.c
+++ b/video/out/hwdec/dmabuf_interop_pl.c
@@ -110,7 +110,7 @@ static bool vaapi_pl_map(struct ra_hwdec_mapper *mapper,
static void vaapi_pl_unmap(struct ra_hwdec_mapper *mapper)
{
- for (int n = 0; n < 4; n++)
+ for (int n = 0; n < MP_ARRAY_SIZE(mapper->tex); n++)
ra_tex_free(mapper->ra, &mapper->tex[n]);
}
diff --git a/video/out/hwdec/hwdec_aimagereader.c b/video/out/hwdec/hwdec_aimagereader.c
index 0dd5497..1aa92ee 100644
--- a/video/out/hwdec/hwdec_aimagereader.c
+++ b/video/out/hwdec/hwdec_aimagereader.c
@@ -75,7 +75,7 @@ struct priv {
void (EGLAPIENTRY *EGLImageTargetTexture2DOES)(GLenum, GLeglImageOES);
};
-const static struct { const char *symbol; int offset; } lib_functions[] = {
+static const struct { const char *symbol; int offset; } lib_functions[] = {
{ "AImageReader_newWithUsage", offsetof(struct priv_owner, AImageReader_newWithUsage) },
{ "AImageReader_getWindow", offsetof(struct priv_owner, AImageReader_getWindow) },
{ "AImageReader_setImageListener", offsetof(struct priv_owner, AImageReader_setImageListener) },
@@ -138,6 +138,10 @@ static int init(struct ra_hwdec *hw)
if (!gl_check_extension(exts, "EGL_ANDROID_image_native_buffer"))
return -1;
+ JNIEnv *env = MP_JNI_GET_ENV(hw);
+ if (!env)
+ return -1;
+
if (!load_lib_functions(p, hw->log))
return -1;
@@ -167,8 +171,6 @@ static int init(struct ra_hwdec *hw)
}
assert(window);
- JNIEnv *env = MP_JNI_GET_ENV(hw);
- assert(env);
jobject surface = p->ANativeWindow_toSurface(env, window);
p->surface = (*env)->NewGlobalRef(env, surface);
(*env)->DeleteLocalRef(env, surface);
@@ -192,10 +194,10 @@ static int init(struct ra_hwdec *hw)
static void uninit(struct ra_hwdec *hw)
{
struct priv_owner *p = hw->priv;
- JNIEnv *env = MP_JNI_GET_ENV(hw);
- assert(env);
if (p->surface) {
+ JNIEnv *env = MP_JNI_GET_ENV(hw);
+ assert(env);
(*env)->DeleteGlobalRef(env, p->surface);
p->surface = NULL;
}
diff --git a/video/out/hwdec/hwdec_cuda.c b/video/out/hwdec/hwdec_cuda.c
index 68ad60d..57e4fb4 100644
--- a/video/out/hwdec/hwdec_cuda.c
+++ b/video/out/hwdec/hwdec_cuda.c
@@ -57,7 +57,7 @@ int check_cu(const struct ra_hwdec *hw, CUresult err, const char *func)
#define CHECK_CU(x) check_cu(hw, (x), #x)
-const static cuda_interop_init interop_inits[] = {
+static const cuda_interop_init interop_inits[] = {
#if HAVE_GL
cuda_gl_init,
#endif
diff --git a/video/out/hwdec/hwdec_drmprime.c b/video/out/hwdec/hwdec_drmprime.c
index f7c6250..bf60405 100644
--- a/video/out/hwdec/hwdec_drmprime.c
+++ b/video/out/hwdec/hwdec_drmprime.c
@@ -23,6 +23,7 @@
#include <libavutil/hwcontext.h>
#include <libavutil/hwcontext_drm.h>
+#include <libavutil/pixdesc.h>
#include <xf86drm.h>
#include "config.h"
@@ -51,7 +52,7 @@ static void uninit(struct ra_hwdec *hw)
av_buffer_unref(&p->hwctx.av_device_ref);
}
-const static dmabuf_interop_init interop_inits[] = {
+static const dmabuf_interop_init interop_inits[] = {
#if HAVE_DMABUF_INTEROP_GL
dmabuf_interop_gl_init,
#endif
@@ -64,6 +65,18 @@ const static dmabuf_interop_init interop_inits[] = {
NULL
};
+/**
+ * Due to the fact that Raspberry Pi support only exists in forked ffmpegs and
+ * also requires custom pixel formats, we need some way to work with those formats
+ * without introducing any build time dependencies. We do this by looking up the
+ * pixel formats by name. As rpi is an important target platform for this hwdec
+ * we don't really have the luxury of ignoring these forks.
+ */
+static const char *forked_pix_fmt_names[] = {
+ "rpi4_8",
+ "rpi4_10",
+};
+
static int init(struct ra_hwdec *hw)
{
struct priv_owner *p = hw->priv;
@@ -119,6 +132,18 @@ static int init(struct ra_hwdec *hw)
MP_TARRAY_APPEND(p, p->formats, num_formats, IMGFMT_NV12);
MP_TARRAY_APPEND(p, p->formats, num_formats, IMGFMT_420P);
MP_TARRAY_APPEND(p, p->formats, num_formats, pixfmt2imgfmt(AV_PIX_FMT_NV16));
+ MP_TARRAY_APPEND(p, p->formats, num_formats, IMGFMT_P010);
+#ifdef AV_PIX_FMT_P210
+ MP_TARRAY_APPEND(p, p->formats, num_formats, pixfmt2imgfmt(AV_PIX_FMT_P210));
+#endif
+
+ for (int i = 0; i < MP_ARRAY_SIZE(forked_pix_fmt_names); i++) {
+ enum AVPixelFormat fmt = av_get_pix_fmt(forked_pix_fmt_names[i]);
+ if (fmt != AV_PIX_FMT_NONE) {
+ MP_TARRAY_APPEND(p, p->formats, num_formats, pixfmt2imgfmt(fmt));
+ }
+ }
+
MP_TARRAY_APPEND(p, p->formats, num_formats, 0); // terminate it
p->hwctx.hw_imgfmt = IMGFMT_DRMPRIME;
diff --git a/video/out/hwdec/hwdec_vaapi.c b/video/out/hwdec/hwdec_vaapi.c
index d8a4517..34b6e52 100644
--- a/video/out/hwdec/hwdec_vaapi.c
+++ b/video/out/hwdec/hwdec_vaapi.c
@@ -124,7 +124,7 @@ static void uninit(struct ra_hwdec *hw)
va_destroy(p->ctx);
}
-const static dmabuf_interop_init interop_inits[] = {
+static const dmabuf_interop_init interop_inits[] = {
#if HAVE_DMABUF_INTEROP_GL
dmabuf_interop_gl_init,
#endif
@@ -261,10 +261,10 @@ static int mapper_init(struct ra_hwdec_mapper *mapper)
return 0;
}
-static void close_file_descriptors(VADRMPRIMESurfaceDescriptor desc)
+static void close_file_descriptors(const VADRMPRIMESurfaceDescriptor *desc)
{
- for (int i = 0; i < desc.num_objects; i++)
- close(desc.objects[i].fd);
+ for (int i = 0; i < desc->num_objects; i++)
+ close(desc->objects[i].fd);
}
static int mapper_map(struct ra_hwdec_mapper *mapper)
@@ -285,7 +285,7 @@ static int mapper_map(struct ra_hwdec_mapper *mapper)
if (!CHECK_VA_STATUS_LEVEL(mapper, "vaExportSurfaceHandle()",
p_owner->probing_formats ? MSGL_DEBUG : MSGL_ERR))
{
- close_file_descriptors(desc);
+ close_file_descriptors(&desc);
goto err;
}
vaSyncSurface(display, va_surface_id(mapper->src));
diff --git a/video/out/hwdec/hwdec_vt.c b/video/out/hwdec/hwdec_vt.c
index ab41d02..643ff90 100644
--- a/video/out/hwdec/hwdec_vt.c
+++ b/video/out/hwdec/hwdec_vt.c
@@ -36,7 +36,7 @@ static void uninit(struct ra_hwdec *hw)
av_buffer_unref(&p->hwctx.av_device_ref);
}
-const static vt_interop_init interop_inits[] = {
+static const vt_interop_init interop_inits[] = {
#if HAVE_VIDEOTOOLBOX_GL || HAVE_IOS_GL
vt_gl_init,
#endif
diff --git a/video/out/mac/common.swift b/video/out/mac/common.swift
index aac7050..594a4b8 100644
--- a/video/out/mac/common.swift
+++ b/video/out/mac/common.swift
@@ -19,11 +19,13 @@ import Cocoa
import IOKit.pwr_mgt
class Common: NSObject {
- var mpv: MPVHelper?
+ var option: OptionHelper
+ var input: InputHelper?
var log: LogHelper
+ var vo: UnsafeMutablePointer<vo>?
let queue: DispatchQueue = DispatchQueue(label: "io.mpv.queue")
- var window: Window?
+ @objc var window: Window?
var view: View?
var titleBar: TitleBar?
@@ -46,39 +48,26 @@ class Common: NSObject {
didSet { if let window = window { window.title = title } }
}
- init(_ mpLog: OpaquePointer?) {
- log = LogHelper(mpLog)
+ init(_ option: OptionHelper, _ log: LogHelper) {
+ self.option = option
+ self.log = log
}
func initMisc(_ vo: UnsafeMutablePointer<vo>) {
- guard let mpv = mpv else {
- log.sendError("Something went wrong, no MPVHelper was initialized")
- exit(1)
- }
-
startDisplayLink(vo)
initLightSensor()
addDisplayReconfigureObserver()
addAppNotifications()
- mpv.setMacOptionCallback(macOptsWakeupCallback, context: self)
+ option.setMacOptionCallback(macOptsWakeupCallback, context: self)
}
func initApp() {
- guard let mpv = mpv else {
- log.sendError("Something went wrong, no MPVHelper was initialized")
- exit(1)
- }
-
var policy: NSApplication.ActivationPolicy = .regular
- switch mpv.macOpts.macos_app_activation_policy {
- case 0:
- policy = .regular
- case 1:
- policy = .accessory
- case 2:
- policy = .prohibited
- default:
- break
+ switch option.mac.macos_app_activation_policy {
+ case 0: policy = .regular
+ case 1: policy = .accessory
+ case 2: policy = .prohibited
+ default: break
}
NSApp.setActivationPolicy(policy)
@@ -86,63 +75,67 @@ class Common: NSObject {
}
func initWindow(_ vo: UnsafeMutablePointer<vo>, _ previousActiveApp: NSRunningApplication?) {
- let (mpv, targetScreen, wr) = getInitProperties(vo)
+ let (targetScreen, wr) = getInitProperties(vo)
guard let view = self.view else {
- log.sendError("Something went wrong, no View was initialized")
+ log.error("Something went wrong, no View was initialized")
exit(1)
}
window = Window(contentRect: wr, screen: targetScreen, view: view, common: self)
guard let window = self.window else {
- log.sendError("Something went wrong, no Window was initialized")
+ log.error("Something went wrong, no Window was initialized")
exit(1)
}
- window.setOnTop(Bool(mpv.opts.ontop), Int(mpv.opts.ontop_level))
- window.setOnAllWorkspaces(Bool(mpv.opts.all_workspaces))
- window.keepAspect = Bool(mpv.opts.keepaspect_window)
+ window.setOnTop(Bool(option.vo.ontop), Int(option.vo.ontop_level))
+ window.setOnAllWorkspaces(Bool(option.vo.all_workspaces))
+ window.keepAspect = Bool(option.vo.keepaspect_window)
window.title = title
- window.border = Bool(mpv.opts.border)
+ window.border = Bool(option.vo.border)
titleBar = TitleBar(frame: wr, window: window, common: self)
- let minimized = Bool(mpv.opts.window_minimized)
+ let maximized = Bool(option.vo.window_maximized)
+ let minimized = Bool(option.vo.window_minimized)
window.isRestorable = false
window.isReleasedWhenClosed = false
- window.setMaximized(minimized ? false : Bool(mpv.opts.window_maximized))
+ window.setMaximized((minimized || !maximized) ? window.isZoomed : maximized)
window.setMinimized(minimized)
window.makeMain()
window.makeKey()
+ view.layer?.contentsScale = window.backingScaleFactor
+
if !minimized {
window.orderFront(nil)
}
- NSApp.activate(ignoringOtherApps: mpv.opts.focus_on_open)
+ NSApp.activate(ignoringOtherApps: option.vo.focus_on >= 1)
// workaround for macOS 10.15 to refocus the previous App
- if (!mpv.opts.focus_on_open) {
- previousActiveApp?.activate(options: .activateAllWindows)
+ if option.vo.focus_on == 0 {
+ previousActiveApp?.activate()
}
}
func initView(_ vo: UnsafeMutablePointer<vo>, _ layer: CALayer) {
- let (_, _, wr) = getInitProperties(vo)
+ let (_, wr) = getInitProperties(vo)
view = View(frame: wr, common: self)
guard let view = self.view else {
- log.sendError("Something went wrong, no View was initialized")
+ log.error("Something went wrong, no View was initialized")
exit(1)
}
view.layer = layer
view.wantsLayer = true
view.layerContentsPlacement = .scaleProportionallyToFit
+ layer.delegate = view
}
func initWindowState() {
- if mpv?.opts.fullscreen ?? false {
+ if option.vo.fullscreen {
DispatchQueue.main.async {
self.window?.toggleFullScreen(nil)
}
@@ -179,7 +172,7 @@ class Common: NSObject {
guard let screen = getTargetScreen(forFullscreen: false) ?? NSScreen.main,
let link = self.link else
{
- log.sendWarning("Couldn't start DisplayLink, no MPVHelper, Screen or DisplayLink available")
+ log.warning("Couldn't start DisplayLink, no Screen or DisplayLink available")
return
}
@@ -198,7 +191,7 @@ class Common: NSObject {
func updateDisplaylink() {
guard let screen = window?.screen, let link = self.link else {
- log.sendWarning("Couldn't update DisplayLink, no Screen or DisplayLink available")
+ log.warning("Couldn't update DisplayLink, no Screen or DisplayLink available")
return
}
@@ -221,17 +214,17 @@ class Common: NSObject {
}
if fabs(actualFps - nominalFps) > 0.1 {
- log.sendVerbose("Falling back to nominal display refresh rate: \(nominalFps)")
+ log.verbose("Falling back to nominal display refresh rate: \(nominalFps)")
return nominalFps
} else {
return actualFps
}
}
} else {
- log.sendWarning("No DisplayLink available")
+ log.warning("No DisplayLink available")
}
- log.sendWarning("Falling back to standard display refresh rate: 60Hz")
+ log.warning("Falling back to standard display refresh rate: 60Hz")
return 60.0
}
@@ -285,28 +278,28 @@ class Common: NSObject {
}
func lightSensorUpdate() {
- log.sendWarning("lightSensorUpdate not implemented")
+ log.warning("lightSensorUpdate not implemented")
}
func initLightSensor() {
let srv = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleLMUController"))
if srv == IO_OBJECT_NULL {
- log.sendVerbose("Can't find an ambient light sensor")
+ log.verbose("Can't find an ambient light sensor")
return
}
lightSensorIOPort = IONotificationPortCreate(kIOMasterPortDefault)
IONotificationPortSetDispatchQueue(lightSensorIOPort, queue)
var n = io_object_t()
- IOServiceAddInterestNotification(lightSensorIOPort, srv, kIOGeneralInterest, lightSensorCallback, MPVHelper.bridge(obj: self), &n)
+ IOServiceAddInterestNotification(lightSensorIOPort, srv, kIOGeneralInterest, lightSensorCallback, TypeHelper.bridge(obj: self), &n)
let kr = IOServiceOpen(srv, mach_task_self_, 0, &lightSensor)
IOObjectRelease(srv)
if kr != KERN_SUCCESS {
- log.sendVerbose("Can't start ambient light sensor connection")
+ log.verbose("Can't start ambient light sensor connection")
return
}
- lightSensorCallback(MPVHelper.bridge(obj: self), 0, 0, nil)
+ lightSensorCallback(TypeHelper.bridge(obj: self), 0, 0, nil)
}
func uninitLightSensor() {
@@ -322,18 +315,18 @@ class Common: NSObject {
let displayID = com.window?.screen?.displayID ?? display
if displayID == display {
- com.log.sendVerbose("Detected display mode change, updating screen refresh rate")
+ com.log.verbose("Detected display mode change, updating screen refresh rate")
com.flagEvents(VO_EVENT_WIN_STATE)
}
}
}
func addDisplayReconfigureObserver() {
- CGDisplayRegisterReconfigurationCallback(reconfigureCallback, MPVHelper.bridge(obj: self))
+ CGDisplayRegisterReconfigurationCallback(reconfigureCallback, TypeHelper.bridge(obj: self))
}
func removeDisplayReconfigureObserver() {
- CGDisplayRemoveReconfigurationCallback(reconfigureCallback, MPVHelper.bridge(obj: self))
+ CGDisplayRemoveReconfigurationCallback(reconfigureCallback, TypeHelper.bridge(obj: self))
}
func addAppNotifications() {
@@ -365,10 +358,8 @@ class Common: NSObject {
}
func setAppIcon() {
- if let app = NSApp as? Application,
- ProcessInfo.processInfo.environment["MPVBUNDLE"] != "true"
- {
- NSApp.applicationIconImage = app.getMPVIcon()
+ if ProcessInfo.processInfo.environment["MPVBUNDLE"] != "true" {
+ NSApp.applicationIconImage = AppHub.shared.getIcon()
}
}
@@ -381,12 +372,12 @@ class Common: NSObject {
}
func updateICCProfile() {
- log.sendWarning("updateICCProfile not implemented")
+ log.warning("updateICCProfile not implemented")
}
func getScreenBy(id screenID: Int) -> NSScreen? {
if screenID >= NSScreen.screens.count {
- log.sendInfo("Screen ID \(screenID) does not exist, falling back to current device")
+ log.info("Screen ID \(screenID) does not exist, falling back to current device")
return nil
} else if screenID < 0 {
return nil
@@ -404,14 +395,9 @@ class Common: NSObject {
}
func getTargetScreen(forFullscreen fs: Bool) -> NSScreen? {
- guard let mpv = mpv else {
- log.sendWarning("Unexpected nil value in getTargetScreen")
- return nil
- }
-
- let screenID = fs ? mpv.opts.fsscreen_id : mpv.opts.screen_id
+ let screenID = fs ? option.vo.fsscreen_id : option.vo.screen_id
var name: String?
- if let screenName = fs ? mpv.opts.fsscreen_name : mpv.opts.screen_name {
+ if let screenName = fs ? option.vo.fsscreen_name : option.vo.screen_name {
name = String(cString: screenName)
}
return getScreenBy(id: Int(screenID)) ?? getScreenBy(name: name)
@@ -426,7 +412,7 @@ class Common: NSObject {
func getWindowGeometry(forScreen screen: NSScreen,
videoOut vo: UnsafeMutablePointer<vo>) -> NSRect {
let r = screen.convertRectToBacking(screen.frame)
- let targetFrame = (mpv?.macOpts.macos_geometry_calculation ?? Int32(FRAME_VISIBLE)) == FRAME_VISIBLE
+ let targetFrame = option.mac.macos_geometry_calculation == FRAME_VISIBLE
? screen.visibleFrame : screen.frame
let rv = screen.convertRectToBacking(targetFrame)
@@ -453,19 +439,15 @@ class Common: NSObject {
return screen.convertRectFromBacking(NSMakeRect(x, y, width, height))
}
- func getInitProperties(_ vo: UnsafeMutablePointer<vo>) -> (MPVHelper, NSScreen, NSRect) {
- guard let mpv = mpv else {
- log.sendError("Something went wrong, no MPVHelper was initialized")
- exit(1)
- }
+ func getInitProperties(_ vo: UnsafeMutablePointer<vo>) -> (NSScreen, NSRect) {
guard let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main else {
- log.sendError("Something went wrong, no Screen was found")
+ log.error("Something went wrong, no Screen was found")
exit(1)
}
let wr = getWindowGeometry(forScreen: targetScreen, videoOut: vo)
- return (mpv, targetScreen, wr)
+ return (targetScreen, wr)
}
// call before initApp, because on macOS +10.15 it changes the active App
@@ -478,11 +460,11 @@ class Common: NSObject {
events |= ev
eventsLock.unlock()
- guard let vout = mpv?.vo else {
- log.sendWarning("vo nil in flagEvents")
+ guard let vo = vo else {
+ log.warning("vo nil in flagEvents")
return
}
- vo_wakeup(vout)
+ vo_wakeup(vo)
}
func checkEvents() -> Int {
@@ -510,47 +492,54 @@ class Common: NSObject {
request: UInt32,
data: UnsafeMutableRawPointer?) -> Int32
{
- guard let mpv = mpv else {
- log.sendWarning("Unexpected nil value in Control Callback")
- return VO_FALSE
- }
-
switch mp_voctrl(request) {
case VOCTRL_CHECK_EVENTS:
events.pointee |= Int32(checkEvents())
return VO_TRUE
case VOCTRL_VO_OPTS_CHANGED:
var opt: UnsafeMutableRawPointer?
- while mpv.nextChangedOption(property: &opt) {
+ while option.nextChangedOption(property: &opt) {
switch opt {
- case MPVHelper.getPointer(&mpv.optsPtr.pointee.border):
+ case TypeHelper.toPointer(&option.voPtr.pointee.border):
DispatchQueue.main.async {
- self.window?.border = Bool(mpv.opts.border)
+ self.window?.border = Bool(self.option.vo.border)
}
- case MPVHelper.getPointer(&mpv.optsPtr.pointee.fullscreen):
+ case TypeHelper.toPointer(&option.voPtr.pointee.fullscreen):
DispatchQueue.main.async {
self.window?.toggleFullScreen(nil)
}
- case MPVHelper.getPointer(&mpv.optsPtr.pointee.ontop): fallthrough
- case MPVHelper.getPointer(&mpv.optsPtr.pointee.ontop_level):
+ case TypeHelper.toPointer(&option.voPtr.pointee.ontop): fallthrough
+ case TypeHelper.toPointer(&option.voPtr.pointee.ontop_level):
+ DispatchQueue.main.async {
+ self.window?.setOnTop(Bool(self.option.vo.ontop), Int(self.option.vo.ontop_level))
+ }
+ case TypeHelper.toPointer(&option.voPtr.pointee.all_workspaces):
DispatchQueue.main.async {
- self.window?.setOnTop(Bool(mpv.opts.ontop), Int(mpv.opts.ontop_level))
+ self.window?.setOnAllWorkspaces(Bool(self.option.vo.all_workspaces))
}
- case MPVHelper.getPointer(&mpv.optsPtr.pointee.all_workspaces):
+ case TypeHelper.toPointer(&option.voPtr.pointee.keepaspect_window):
DispatchQueue.main.async {
- self.window?.setOnAllWorkspaces(Bool(mpv.opts.all_workspaces))
+ self.window?.keepAspect = Bool(self.option.vo.keepaspect_window)
}
- case MPVHelper.getPointer(&mpv.optsPtr.pointee.keepaspect_window):
+ case TypeHelper.toPointer(&option.voPtr.pointee.window_minimized):
DispatchQueue.main.async {
- self.window?.keepAspect = Bool(mpv.opts.keepaspect_window)
+ self.window?.setMinimized(Bool(self.option.vo.window_minimized))
}
- case MPVHelper.getPointer(&mpv.optsPtr.pointee.window_minimized):
+ case TypeHelper.toPointer(&option.voPtr.pointee.window_maximized):
DispatchQueue.main.async {
- self.window?.setMinimized(Bool(mpv.opts.window_minimized))
+ self.window?.setMaximized(Bool(self.option.vo.window_maximized))
}
- case MPVHelper.getPointer(&mpv.optsPtr.pointee.window_maximized):
+ case TypeHelper.toPointer(&option.voPtr.pointee.cursor_passthrough):
DispatchQueue.main.async {
- self.window?.setMaximized(Bool(mpv.opts.window_maximized))
+ self.window?.ignoresMouseEvents = self.option.vo.cursor_passthrough
+ }
+ case TypeHelper.toPointer(&option.voPtr.pointee.geometry): fallthrough
+ case TypeHelper.toPointer(&option.voPtr.pointee.autofit): fallthrough
+ case TypeHelper.toPointer(&option.voPtr.pointee.autofit_smaller): fallthrough
+ case TypeHelper.toPointer(&option.voPtr.pointee.autofit_larger):
+ DispatchQueue.main.async {
+ let (_, wr) = self.getInitProperties(vo)
+ self.window?.updateFrame(wr)
}
default:
break
@@ -561,6 +550,13 @@ class Common: NSObject {
let fps = data!.assumingMemoryBound(to: CDouble.self)
fps.pointee = currentFps()
return VO_TRUE
+ case VOCTRL_GET_WINDOW_ID:
+ guard let window = window else {
+ return VO_NOTAVAIL
+ }
+ let wid = data!.assumingMemoryBound(to: Int64.self)
+ wid.pointee = unsafeBitCast(window, to: Int64.self)
+ return VO_TRUE
case VOCTRL_GET_HIDPI_SCALE:
let scaleFactor = data!.assumingMemoryBound(to: CDouble.self)
let screen = getCurrentScreen()
@@ -584,7 +580,7 @@ class Common: NSObject {
case VOCTRL_GET_ICC_PROFILE:
let screen = getCurrentScreen()
guard var iccData = screen?.colorSpace?.iccProfileData else {
- log.sendWarning("No Screen available to retrieve ICC profile")
+ log.warning("No Screen available to retrieve ICC profile")
return VO_TRUE
}
@@ -605,10 +601,8 @@ class Common: NSObject {
case VOCTRL_GET_UNFS_WINDOW_SIZE:
let sizeData = data!.assumingMemoryBound(to: Int32.self)
let size = UnsafeMutableBufferPointer(start: sizeData, count: 2)
- var rect = window?.unfsContentFrame ?? NSRect(x: 0, y: 0, width: 1280, height: 720)
- if let screen = window?.currentScreen, !Bool(mpv.opts.hidpi_window_scale) {
- rect = screen.convertRectToBacking(rect)
- }
+ let rect = (Bool(option.vo.hidpi_window_scale) ? window?.unfsContentFrame
+ : window?.unfsContentFramePixel) ?? NSRect(x: 0, y: 0, width: 1280, height: 720)
size[0] = Int32(rect.size.width)
size[1] = Int32(rect.size.height)
@@ -618,7 +612,7 @@ class Common: NSObject {
let size = UnsafeBufferPointer(start: sizeData, count: 2)
var rect = NSMakeRect(0, 0, CGFloat(size[0]), CGFloat(size[1]))
DispatchQueue.main.async {
- if let screen = self.window?.currentScreen, !Bool(self.mpv?.opts.hidpi_window_scale ?? true) {
+ if let screen = self.window?.currentScreen, !Bool(self.option.vo.hidpi_window_scale) {
rect = screen.convertRectFromBacking(rect)
}
self.window?.updateSize(rect.size)
@@ -630,13 +624,13 @@ class Common: NSObject {
var count: Int32 = 0
let displayName = getCurrentScreen()?.localizedName ?? "Unknown"
- SWIFT_TARRAY_STRING_APPEND(nil, &array, &count, ta_xstrdup(nil, displayName))
- SWIFT_TARRAY_STRING_APPEND(nil, &array, &count, nil)
+ app_bridge_tarray_append(nil, &array, &count, ta_xstrdup(nil, displayName))
+ app_bridge_tarray_append(nil, &array, &count, nil)
dnames.pointee = array
return VO_TRUE
case VOCTRL_GET_DISPLAY_RES:
guard let screen = getCurrentScreen() else {
- log.sendWarning("No Screen available to retrieve frame")
+ log.warning("No Screen available to retrieve frame")
return VO_NOTAVAIL
}
let sizeData = data!.assumingMemoryBound(to: Int32.self)
@@ -650,10 +644,9 @@ class Common: NSObject {
focus.pointee = NSApp.isActive
return VO_TRUE
case VOCTRL_UPDATE_WINDOW_TITLE:
- let titleData = data!.assumingMemoryBound(to: Int8.self)
+ let title = String(cString: data!.assumingMemoryBound(to: CChar.self))
DispatchQueue.main.async {
- let title = NSString(utf8String: titleData) as String?
- self.title = title ?? "Unknown Title"
+ self.title = title
}
return VO_TRUE
default:
@@ -669,20 +662,15 @@ class Common: NSObject {
}
func macOptsUpdate() {
- guard let mpv = mpv else {
- log.sendWarning("Unexpected nil value in mac opts update")
- return
- }
-
var opt: UnsafeMutableRawPointer?
- while mpv.nextChangedMacOption(property: &opt) {
+ while option.nextChangedMacOption(property: &opt) {
switch opt {
- case MPVHelper.getPointer(&mpv.macOptsPtr.pointee.macos_title_bar_appearance):
- titleBar?.set(appearance: Int(mpv.macOpts.macos_title_bar_appearance))
- case MPVHelper.getPointer(&mpv.macOptsPtr.pointee.macos_title_bar_material):
- titleBar?.set(material: Int(mpv.macOpts.macos_title_bar_material))
- case MPVHelper.getPointer(&mpv.macOptsPtr.pointee.macos_title_bar_color):
- titleBar?.set(color: mpv.macOpts.macos_title_bar_color)
+ case TypeHelper.toPointer(&option.macPtr.pointee.macos_title_bar_appearance):
+ titleBar?.set(appearance: Int(option.mac.macos_title_bar_appearance))
+ case TypeHelper.toPointer(&option.macPtr.pointee.macos_title_bar_material):
+ titleBar?.set(material: Int(option.mac.macos_title_bar_material))
+ case TypeHelper.toPointer(&option.macPtr.pointee.macos_title_bar_color):
+ titleBar?.set(color: option.mac.macos_title_bar_color)
default:
break
}
diff --git a/video/out/mac/gl_layer.swift b/video/out/mac/gl_layer.swift
index dd96af7..38320bc 100644
--- a/video/out/mac/gl_layer.swift
+++ b/video/out/mac/gl_layer.swift
@@ -82,8 +82,6 @@ class GLLayer: CAOpenGLLayer {
enum Draw: Int { case normal = 1, atomic, atomicEnd }
var draw: Draw = .normal
- let queue: DispatchQueue = DispatchQueue(label: "io.mpv.queue.draw")
-
var needsICCUpdate: Bool = false {
didSet {
if needsICCUpdate == true {
@@ -199,6 +197,14 @@ class GLLayer: CAOpenGLLayer {
}
}
+ func lockCglContext() {
+ CGLLockContext(cglContext)
+ }
+
+ func unlockCglContext() {
+ CGLUnlockContext(cglContext)
+ }
+
override func copyCGLPixelFormat(forDisplayMask mask: UInt32) -> CGLPixelFormatObj {
return cglPixelFormat
}
@@ -219,17 +225,19 @@ class GLLayer: CAOpenGLLayer {
super.display()
CATransaction.flush()
if isUpdate && needsFlip {
+ lockCglContext()
CGLSetCurrentContext(cglContext)
if libmpv.isRenderUpdateFrame() {
libmpv.drawRender(NSZeroSize, bufferDepth, cglContext, skip: true)
}
+ unlockCglContext()
}
displayLock.unlock()
}
func update(force: Bool = false) {
if force { forceDraw = true }
- queue.async {
+ DispatchQueue.main.async {
if self.forceDraw || !self.inLiveResize {
self.needsFlip = true
self.display()
@@ -241,7 +249,7 @@ class GLLayer: CAOpenGLLayer {
var pix: CGLPixelFormatObj?
var depth: GLint = 8
var err: CGLError = CGLError(rawValue: 0)
- let swRender = ccb.libmpv.macOpts.cocoa_cb_sw_renderer
+ let swRender = ccb.option.mac.cocoa_cb_sw_renderer
if swRender != 1 {
(pix, depth, err) = GLLayer.findPixelFormat(ccb)
@@ -252,7 +260,7 @@ class GLLayer: CAOpenGLLayer {
}
guard let pixelFormat = pix, err == kCGLNoError else {
- ccb.log.sendError("Couldn't create any CGL pixel format")
+ ccb.log.error("Couldn't create any CGL pixel format")
exit(1)
}
@@ -269,12 +277,12 @@ class GLLayer: CAOpenGLLayer {
glBase.insert(CGLPixelFormatAttribute(ver.rawValue), at: 1)
var glFormat = [glBase]
- if ccb.libmpv.macOpts.cocoa_cb_10bit_context {
+ if ccb.option.mac.cocoa_cb_10bit_context {
glFormat += [glFormat10Bit]
}
glFormat += glFormatOptional
- if !ccb.libmpv.macOpts.macos_force_dedicated_gpu {
+ if !ccb.option.mac.macos_force_dedicated_gpu {
glFormat += [glFormatAutoGPU]
}
@@ -289,7 +297,7 @@ class GLLayer: CAOpenGLLayer {
return attributeLookUp[value.rawValue] ?? String(value.rawValue)
})
- ccb.log.sendVerbose("Created CGL pixel format with attributes: " +
+ ccb.log.verbose("Created CGL pixel format with attributes: " +
"\(attArray.joined(separator: ", "))")
return (pix, glFormat.contains(glFormat10Bit) ? 16 : 8, err)
}
@@ -297,11 +305,11 @@ class GLLayer: CAOpenGLLayer {
}
let errS = String(cString: CGLErrorString(err))
- ccb.log.sendWarning("Couldn't create a " +
+ ccb.log.warning("Couldn't create a " +
"\(software ? "software" : "hardware accelerated") " +
"CGL pixel format: \(errS) (\(err.rawValue))")
- if software == false && ccb.libmpv.macOpts.cocoa_cb_sw_renderer == -1 {
- ccb.log.sendWarning("Falling back to software renderer")
+ if software == false && ccb.option.mac.cocoa_cb_sw_renderer == -1 {
+ ccb.log.warning("Falling back to software renderer")
}
return (pix, 8, err)
@@ -313,7 +321,7 @@ class GLLayer: CAOpenGLLayer {
guard let cglContext = context, error == kCGLNoError else {
let errS = String(cString: CGLErrorString(error))
- ccb.log.sendError("Couldn't create a CGLContext: " + errS)
+ ccb.log.error("Couldn't create a CGLContext: " + errS)
exit(1)
}
diff --git a/video/out/mac/metal_layer.swift b/video/out/mac/metal_layer.swift
index 7cea87c..7fc419a 100644
--- a/video/out/mac/metal_layer.swift
+++ b/video/out/mac/metal_layer.swift
@@ -16,10 +16,22 @@
*/
import Cocoa
+import QuartzCore
class MetalLayer: CAMetalLayer {
unowned var common: MacCommon
+ // workaround for a MoltenVK workaround that sets the drawableSize to 1x1 to forcefully complete
+ // the presentation, this causes flicker and the drawableSize possibly staying at 1x1
+ override var drawableSize: CGSize {
+ get { return super.drawableSize }
+ set {
+ if Int(newValue.width) > 1 && Int(newValue.height) > 1 {
+ super.drawableSize = newValue
+ }
+ }
+ }
+
init(common com: MacCommon) {
common = com
super.init()
diff --git a/video/out/mac/title_bar.swift b/video/out/mac/title_bar.swift
index 764c1ff..b274100 100644
--- a/video/out/mac/title_bar.swift
+++ b/video/out/mac/title_bar.swift
@@ -19,7 +19,7 @@ import Cocoa
class TitleBar: NSVisualEffectView {
unowned var common: Common
- var mpv: MPVHelper? { get { return common.mpv } }
+ var option: OptionHelper { get { return common.option } }
var systemBar: NSView? {
get { return common.window?.standardWindowButton(.closeButton)?.superview }
@@ -64,9 +64,9 @@ class TitleBar: NSVisualEffectView {
window.contentView?.addSubview(self, positioned: .above, relativeTo: nil)
window.titlebarAppearsTransparent = true
window.styleMask.insert(.fullSizeContentView)
- set(appearance: Int(mpv?.macOpts.macos_title_bar_appearance ?? 0))
- set(material: Int(mpv?.macOpts.macos_title_bar_material ?? 0))
- set(color: mpv?.macOpts.macos_title_bar_color ?? "#00000000")
+ set(appearance: Int(option.mac.macos_title_bar_appearance))
+ set(material: Int(option.mac.macos_title_bar_material))
+ set(color: option.mac.macos_title_bar_color)
}
required init?(coder: NSCoder) {
@@ -195,10 +195,6 @@ class TitleBar: NSVisualEffectView {
default:
return nil
}
-
-
- let style = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
- return appearanceFrom(string: style == nil ? "aqua" : "vibrantDark")
}
func materialFrom(string: String) -> NSVisualEffectView.Material {
@@ -221,9 +217,7 @@ class TitleBar: NSVisualEffectView {
case "15", "light": return .light
case "16", "mediumLight": return .mediumLight
case "17", "ultraDark": return .ultraDark
- default: break
+ default: return .titlebar
}
-
- return .titlebar
}
}
diff --git a/video/out/mac/view.swift b/video/out/mac/view.swift
index c4776c3..047a523 100644
--- a/video/out/mac/view.swift
+++ b/video/out/mac/view.swift
@@ -17,9 +17,9 @@
import Cocoa
-class View: NSView {
+class View: NSView, CALayerDelegate {
unowned var common: Common
- var mpv: MPVHelper? { get { return common.mpv } }
+ var input: InputHelper? { get { return common.input } }
var tracker: NSTrackingArea?
var hasMouseDown: Bool = false
@@ -52,7 +52,7 @@ class View: NSView {
addTrackingArea(tracker!)
if containsMouseLocation() {
- cocoa_put_key_with_modifiers(SWIFT_KEY_MOUSE_LEAVE, 0)
+ input?.put(key: SWIFT_KEY_MOUSE_LEAVE)
}
}
@@ -77,30 +77,24 @@ class View: NSView {
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
let pb = sender.draggingPasteboard
guard let types = pb.types else { return false }
+ var files: [String] = []
if types.contains(.fileURL) || types.contains(.URL) {
- if let urls = pb.readObjects(forClasses: [NSURL.self]) as? [URL] {
- let files = urls.map { $0.absoluteString }
- EventsResponder.sharedInstance().handleFilesArray(files)
- return true
- }
+ guard let urls = pb.readObjects(forClasses: [NSURL.self]) as? [URL] else { return false }
+ files = urls.map { $0.absoluteString }
} else if types.contains(.string) {
guard let str = pb.string(forType: .string) else { return false }
- var filesArray: [String] = []
-
- for val in str.components(separatedBy: "\n") {
- let url = val.trimmingCharacters(in: .whitespacesAndNewlines)
+ files = str.components(separatedBy: "\n").compactMap {
+ let url = $0.trimmingCharacters(in: .whitespacesAndNewlines)
let path = (url as NSString).expandingTildeInPath
- if isURL(url) {
- filesArray.append(url)
- } else if path.starts(with: "/") {
- filesArray.append(path)
- }
+ if isURL(url) { return url }
+ if path.starts(with: "/") { return path }
+ return nil
}
- EventsResponder.sharedInstance().handleFilesArray(filesArray)
- return true
}
- return false
+ if files.isEmpty { return false }
+ input?.open(files: files)
+ return true
}
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
@@ -116,94 +110,66 @@ class View: NSView {
}
override func mouseEntered(with event: NSEvent) {
- if mpv?.mouseEnabled() ?? true {
- cocoa_put_key_with_modifiers(SWIFT_KEY_MOUSE_ENTER, 0)
+ if input?.mouseEnabled() ?? true {
+ input?.put(key: SWIFT_KEY_MOUSE_ENTER)
}
common.updateCursorVisibility()
}
override func mouseExited(with event: NSEvent) {
- if mpv?.mouseEnabled() ?? true {
- cocoa_put_key_with_modifiers(SWIFT_KEY_MOUSE_LEAVE, 0)
+ if input?.mouseEnabled() ?? true {
+ input?.put(key: SWIFT_KEY_MOUSE_LEAVE)
}
common.titleBar?.hide()
common.setCursorVisibility(true)
}
override func mouseMoved(with event: NSEvent) {
- if mpv?.mouseEnabled() ?? true {
- signalMouseMovement(event)
- }
+ signalMouseMovement(event)
common.titleBar?.show()
}
override func mouseDragged(with event: NSEvent) {
- if mpv?.mouseEnabled() ?? true {
- signalMouseMovement(event)
- }
+ signalMouseMovement(event)
}
override func mouseDown(with event: NSEvent) {
- if mpv?.mouseEnabled() ?? true {
- signalMouseDown(event)
- }
+ hasMouseDown = event.clickCount <= 1
+ input?.processMouse(event: event)
}
override func mouseUp(with event: NSEvent) {
- if mpv?.mouseEnabled() ?? true {
- signalMouseUp(event)
- }
+ hasMouseDown = false
common.window?.isMoving = false
+ input?.processMouse(event: event)
}
override func rightMouseDown(with event: NSEvent) {
- if mpv?.mouseEnabled() ?? true {
- signalMouseDown(event)
- }
+ hasMouseDown = event.clickCount <= 1
+ input?.processMouse(event: event)
}
override func rightMouseUp(with event: NSEvent) {
- if mpv?.mouseEnabled() ?? true {
- signalMouseUp(event)
- }
+ hasMouseDown = false
+ input?.processMouse(event: event)
}
override func otherMouseDown(with event: NSEvent) {
- if mpv?.mouseEnabled() ?? true {
- signalMouseDown(event)
- }
+ hasMouseDown = event.clickCount <= 1
+ input?.processMouse(event: event)
}
override func otherMouseUp(with event: NSEvent) {
- if mpv?.mouseEnabled() ?? true {
- signalMouseUp(event)
- }
+ hasMouseDown = false
+ input?.processMouse(event: event)
}
override func magnify(with event: NSEvent) {
- event.phase == .ended ?
- common.windowDidEndLiveResize() : common.windowWillStartLiveResize()
-
+ common.window?.isAnimating = event.phase != .ended
+ event.phase == .ended ? common.windowDidEndLiveResize() : common.windowWillStartLiveResize()
common.window?.addWindowScale(Double(event.magnification))
}
- func signalMouseDown(_ event: NSEvent) {
- signalMouseEvent(event, MP_KEY_STATE_DOWN)
- if event.clickCount > 1 {
- signalMouseEvent(event, MP_KEY_STATE_UP)
- }
- }
-
- func signalMouseUp(_ event: NSEvent) {
- signalMouseEvent(event, MP_KEY_STATE_UP)
- }
-
- func signalMouseEvent(_ event: NSEvent, _ state: UInt32) {
- hasMouseDown = state == MP_KEY_STATE_DOWN
- let mpkey = getMpvButton(event)
- cocoa_put_key_with_modifiers((mpkey | Int32(state)), Int32(event.modifierFlags.rawValue))
- }
-
func signalMouseMovement(_ event: NSEvent) {
var point = convert(event.locationInWindow, from: nil)
point = convertToBacking(point)
@@ -211,46 +177,12 @@ class View: NSView {
common.window?.updateMovableBackground(point)
if !(common.window?.isMoving ?? false) {
- mpv?.setMousePosition(point)
- }
- }
-
- func preciseScroll(_ event: NSEvent) {
- var delta: Double
- var cmd: Int32
-
- if abs(event.deltaY) >= abs(event.deltaX) {
- delta = Double(event.deltaY) * 0.1
- cmd = delta > 0 ? SWIFT_WHEEL_UP : SWIFT_WHEEL_DOWN
- } else {
- delta = Double(event.deltaX) * 0.1
- cmd = delta > 0 ? SWIFT_WHEEL_LEFT : SWIFT_WHEEL_RIGHT
+ input?.setMouse(position: point)
}
-
- mpv?.putAxis(cmd, delta: abs(delta))
}
override func scrollWheel(with event: NSEvent) {
- if !(mpv?.mouseEnabled() ?? true) {
- return
- }
-
- if event.hasPreciseScrollingDeltas {
- preciseScroll(event)
- } else {
- let modifiers = event.modifierFlags
- let deltaX = modifiers.contains(.shift) ? event.scrollingDeltaY : event.scrollingDeltaX
- let deltaY = modifiers.contains(.shift) ? event.scrollingDeltaX : event.scrollingDeltaY
- var mpkey: Int32
-
- if abs(deltaY) >= abs(deltaX) {
- mpkey = deltaY > 0 ? SWIFT_WHEEL_UP : SWIFT_WHEEL_DOWN
- } else {
- mpkey = deltaX > 0 ? SWIFT_WHEEL_LEFT : SWIFT_WHEEL_RIGHT
- }
-
- cocoa_put_key_with_modifiers(mpkey, Int32(modifiers.rawValue))
- }
+ input?.processWheel(event: event)
}
func containsMouseLocation() -> Bool {
@@ -282,16 +214,4 @@ class View: NSView {
guard let window = common.window else { return false }
return !hasMouseDown && containsMouseLocation() && window.isKeyWindow
}
-
- func getMpvButton(_ event: NSEvent) -> Int32 {
- let buttonNumber = event.buttonNumber
- switch (buttonNumber) {
- case 0: return SWIFT_MBTN_LEFT
- case 1: return SWIFT_MBTN_RIGHT
- case 2: return SWIFT_MBTN_MID
- case 3: return SWIFT_MBTN_BACK
- case 4: return SWIFT_MBTN_FORWARD
- default: return SWIFT_MBTN9 + Int32(buttonNumber - 5)
- }
- }
}
diff --git a/video/out/mac/window.swift b/video/out/mac/window.swift
index 7b1a858..c5a711e 100644
--- a/video/out/mac/window.swift
+++ b/video/out/mac/window.swift
@@ -19,14 +19,15 @@ import Cocoa
class Window: NSWindow, NSWindowDelegate {
weak var common: Common! = nil
- var mpv: MPVHelper? { get { return common.mpv } }
+ var option: OptionHelper { get { return common.option } }
+ var input: InputHelper? { get { return common.input } }
var targetScreen: NSScreen?
var previousScreen: NSScreen?
var currentScreen: NSScreen?
var unfScreen: NSScreen?
- var unfsContentFrame: NSRect?
+ var unfsContentFrame: NSRect = NSRect(x: 0, y: 0, width: 160, height: 90)
var isInFullscreen: Bool = false
var isMoving: Bool = false
var previousStyleMask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable]
@@ -34,8 +35,8 @@ class Window: NSWindow, NSWindowDelegate {
var isAnimating: Bool = false
let animationLock: NSCondition = NSCondition()
- var unfsContentFramePixel: NSRect { get { return convertToBacking(unfsContentFrame ?? NSRect(x: 0, y: 0, width: 160, height: 90)) } }
- var framePixel: NSRect { get { return convertToBacking(frame) } }
+ var unfsContentFramePixel: NSRect { get { return convertToBacking(unfsContentFrame) } }
+ @objc var framePixel: NSRect { get { return convertToBacking(frame) } }
var keepAspect: Bool = true {
didSet {
@@ -44,7 +45,7 @@ class Window: NSWindow, NSWindowDelegate {
}
if keepAspect {
- contentAspectRatio = unfsContentFrame?.size ?? contentAspectRatio
+ contentAspectRatio = unfsContentFrame.size
} else {
resizeIncrements = NSSize(width: 1.0, height: 1.0)
}
@@ -91,7 +92,9 @@ class Window: NSWindow, NSWindowDelegate {
title = com.title
minSize = NSMakeSize(160, 90)
collectionBehavior = .fullScreenPrimary
+ ignoresMouseEvents = option.vo.cursor_passthrough
delegate = self
+ unfsContentFrame = contentRect
if let cView = contentView {
cView.addSubview(view)
@@ -103,13 +106,11 @@ class Window: NSWindow, NSWindowDelegate {
currentScreen = screen
unfScreen = screen
- if let app = NSApp as? Application {
- app.menuBar.register(#selector(setHalfWindowSize), for: MPM_H_SIZE)
- app.menuBar.register(#selector(setNormalWindowSize), for: MPM_N_SIZE)
- app.menuBar.register(#selector(setDoubleWindowSize), for: MPM_D_SIZE)
- app.menuBar.register(#selector(performMiniaturize(_:)), for: MPM_MINIMIZE)
- app.menuBar.register(#selector(performZoom(_:)), for: MPM_ZOOM)
- }
+ AppHub.shared.menu?.register(#selector(setHalfWindowSize), key: .itemHalfSize)
+ AppHub.shared.menu?.register(#selector(setNormalWindowSize), key: .itemNormalSize)
+ AppHub.shared.menu?.register(#selector(setDoubleWindowSize), key: .itemDoubleSize)
+ AppHub.shared.menu?.register(#selector(performMiniaturize(_:)), key: .itemMinimize)
+ AppHub.shared.menu?.register(#selector(performZoom(_:)), key: .itemZoom)
}
override func toggleFullScreen(_ sender: Any?) {
@@ -141,7 +142,7 @@ class Window: NSWindow, NSWindowDelegate {
setFrame(frame, display: true)
}
- if Bool(mpv?.opts.native_fs ?? true) {
+ if Bool(option.vo.native_fs) {
super.toggleFullScreen(sender)
} else {
if !isInFullscreen {
@@ -192,7 +193,7 @@ class Window: NSWindow, NSWindowDelegate {
func windowDidEnterFullScreen(_ notification: Notification) {
isInFullscreen = true
- mpv?.setOption(fullscreen: isInFullscreen)
+ option.setOption(fullscreen: isInFullscreen)
common.updateCursorVisibility()
endAnimation(frame)
common.titleBar?.show()
@@ -201,7 +202,7 @@ class Window: NSWindow, NSWindowDelegate {
func windowDidExitFullScreen(_ notification: Notification) {
guard let tScreen = targetScreen else { return }
isInFullscreen = false
- mpv?.setOption(fullscreen: isInFullscreen)
+ option.setOption(fullscreen: isInFullscreen)
endAnimation(calculateWindowPosition(for: tScreen, withoutBounds: targetScreen == screen))
common.view?.layerContentsPlacement = .scaleProportionallyToFit
}
@@ -249,7 +250,7 @@ class Window: NSWindow, NSWindowDelegate {
setFrame(targetFrame, display: true)
endAnimation()
isInFullscreen = true
- mpv?.setOption(fullscreen: isInFullscreen)
+ option.setOption(fullscreen: isInFullscreen)
common.windowSetToFullScreen()
}
@@ -268,7 +269,7 @@ class Window: NSWindow, NSWindowDelegate {
setFrame(newFrame, display: true)
endAnimation()
isInFullscreen = false
- mpv?.setOption(fullscreen: isInFullscreen)
+ option.setOption(fullscreen: isInFullscreen)
common.windowSetToWindow()
}
@@ -281,7 +282,7 @@ class Window: NSWindow, NSWindowDelegate {
}
func getFsAnimationDuration(_ def: Double) -> Double {
- let duration = mpv?.macOpts.macos_fs_animation_duration ?? -1
+ let duration = option.mac.macos_fs_animation_duration
if duration < 0 {
return def
} else {
@@ -334,7 +335,7 @@ class Window: NSWindow, NSWindowDelegate {
func updateMovableBackground(_ pos: NSPoint) {
if !isInFullscreen {
- isMovableByWindowBackground = mpv?.canBeDraggedAt(pos) ?? true
+ isMovableByWindowBackground = input?.draggable(at: pos) ?? true
} else {
isMovableByWindowBackground = false
}
@@ -342,35 +343,31 @@ class Window: NSWindow, NSWindowDelegate {
func updateFrame(_ rect: NSRect) {
if rect != frame {
- let cRect = frameRect(forContentRect: rect)
unfsContentFrame = rect
- setFrame(cRect, display: true)
- common.windowDidUpdateFrame()
+ if !isInFullscreen {
+ let cRect = frameRect(forContentRect: rect)
+ setFrame(cRect, display: true)
+ common.windowDidUpdateFrame()
+ }
}
}
func updateSize(_ size: NSSize) {
if let currentSize = contentView?.frame.size, size != currentSize {
let newContentFrame = centeredContentSize(for: frame, size: size)
- if !isInFullscreen {
- updateFrame(newContentFrame)
- } else {
- unfsContentFrame = newContentFrame
- }
+ updateFrame(newContentFrame)
}
}
override func setFrame(_ frameRect: NSRect, display flag: Bool) {
if frameRect.width < minSize.width || frameRect.height < minSize.height {
- common.log.sendVerbose("tried to set too small window size: \(frameRect.size)")
+ common.log.verbose("tried to set too small window size: \(frameRect.size)")
return
}
super.setFrame(frameRect, display: flag)
- if let size = unfsContentFrame?.size, keepAspect {
- contentAspectRatio = size
- }
+ if keepAspect { contentAspectRatio = unfsContentFrame.size }
}
func centeredContentSize(for rect: NSRect, size sz: NSSize) -> NSRect {
@@ -391,10 +388,9 @@ class Window: NSWindow, NSWindowDelegate {
}
func calculateWindowPosition(for tScreen: NSScreen, withoutBounds: Bool) -> NSRect {
- guard let contentFrame = unfsContentFrame, let screen = unfScreen else {
- return frame
- }
- var newFrame = frameRect(forContentRect: contentFrame)
+ guard let screen = unfScreen else { return frame }
+
+ var newFrame = frameRect(forContentRect: unfsContentFrame)
let targetFrame = tScreen.frame
let targetVisibleFrame = tScreen.visibleFrame
let unfsScreenFrame = screen.frame
@@ -504,12 +500,12 @@ class Window: NSWindow, NSWindowDelegate {
@objc func setDoubleWindowSize() { setWindowScale(2.0) }
func setWindowScale(_ scale: Double) {
- mpv?.command("set window-scale \(scale)")
+ input?.command("set window-scale \(scale)")
}
func addWindowScale(_ scale: Double) {
if !isInFullscreen {
- mpv?.command("add window-scale \(scale)")
+ input?.command("add current-window-scale \(scale)")
}
}
@@ -542,7 +538,7 @@ class Window: NSWindow, NSWindowDelegate {
func windowDidEndLiveResize(_ notification: Notification) {
common.windowDidEndLiveResize()
- mpv?.setOption(maximized: isZoomed)
+ option.setOption(maximized: isZoomed)
if let contentViewFrame = contentView?.frame,
!isAnimating && !isInFullscreen
@@ -552,20 +548,23 @@ class Window: NSWindow, NSWindowDelegate {
}
func windowDidResize(_ notification: Notification) {
+ if let contentViewFrame = contentView?.frame, !isAnimating && !isInFullscreen && !inLiveResize {
+ unfsContentFrame = convertToScreen(contentViewFrame)
+ }
common.windowDidResize()
}
func windowShouldClose(_ sender: NSWindow) -> Bool {
- cocoa_put_key(MP_KEY_CLOSE_WIN)
+ input?.put(key: MP_KEY_CLOSE_WIN)
return false
}
func windowDidMiniaturize(_ notification: Notification) {
- mpv?.setOption(minimized: true)
+ option.setOption(minimized: true)
}
func windowDidDeminiaturize(_ notification: Notification) {
- mpv?.setOption(minimized: false)
+ option.setOption(minimized: false)
}
func windowDidResignKey(_ notification: Notification) {
@@ -588,6 +587,6 @@ class Window: NSWindow, NSWindowDelegate {
}
func windowDidMove(_ notification: Notification) {
- mpv?.setOption(maximized: isZoomed)
+ option.setOption(maximized: isZoomed)
}
}
diff --git a/video/out/mac_common.swift b/video/out/mac_common.swift
index 349712b..f29815d 100644
--- a/video/out/mac_common.swift
+++ b/video/out/mac_common.swift
@@ -20,16 +20,18 @@ import Cocoa
class MacCommon: Common {
@objc var layer: MetalLayer?
+ var presentation: Presentation?
var timer: PreciseTimer?
var swapTime: UInt64 = 0
let swapLock: NSCondition = NSCondition()
- var needsICCUpdate: Bool = false
-
@objc init(_ vo: UnsafeMutablePointer<vo>) {
- let newlog = mp_log_new(vo, vo.pointee.log, "mac")
- super.init(newlog)
- mpv = MPVHelper(vo, log)
+ let log = LogHelper(mp_log_new(vo, vo.pointee.log, "mac"))
+ let option = OptionHelper(vo, vo.pointee.global)
+ super.init(option, log)
+ self.vo = vo
+ input = InputHelper(vo.pointee.input_ctx, option)
+ presentation = Presentation(common: self)
timer = PreciseTimer(common: self)
DispatchQueue.main.sync {
@@ -39,16 +41,16 @@ class MacCommon: Common {
}
@objc func config(_ vo: UnsafeMutablePointer<vo>) -> Bool {
- mpv?.vo = vo
+ self.vo = vo
DispatchQueue.main.sync {
let previousActiveApp = getActiveApp()
initApp()
- let (_, _, wr) = getInitProperties(vo)
+ let (_, wr) = getInitProperties(vo)
guard let layer = self.layer else {
- log.sendError("Something went wrong, no MetalLayer was initialized")
+ log.error("Something went wrong, no MetalLayer was initialized")
exit(1)
}
@@ -58,12 +60,18 @@ class MacCommon: Common {
initWindowState()
}
- if !NSEqualSizes(window?.unfsContentFramePixel.size ?? NSZeroSize, wr.size) {
+ if !NSEqualSizes(window?.unfsContentFramePixel.size ?? NSZeroSize, wr.size) &&
+ option.vo.auto_window_resize
+ {
window?.updateSize(wr.size)
}
+ if option.vo.focus_on == 2 {
+ NSApp.activate(ignoringOtherApps: true)
+ }
+
windowDidResize()
- needsICCUpdate = true
+ updateICCProfile()
}
return true
@@ -83,7 +91,7 @@ class MacCommon: Common {
}
@objc func swapBuffer() {
- if mpv?.macOpts.macos_render_timer ?? Int32(RENDER_TIMER_CALLBACK) != RENDER_TIMER_SYSTEM {
+ if option.mac.macos_render_timer > RENDER_TIMER_SYSTEM {
swapLock.lock()
while(swapTime < 1) {
swapLock.wait()
@@ -91,18 +99,16 @@ class MacCommon: Common {
swapTime = 0
swapLock.unlock()
}
-
- if needsICCUpdate {
- needsICCUpdate = false
- updateICCProfile()
- }
}
- func updateRenderSize(_ size: NSSize) {
- mpv?.vo.pointee.dwidth = Int32(size.width)
- mpv?.vo.pointee.dheight = Int32(size.height)
- flagEvents(VO_EVENT_RESIZE | VO_EVENT_EXPOSE)
- }
+ @objc func fillVsync(info: UnsafeMutablePointer<vo_vsync_info>) {
+ if option.mac.macos_render_timer != RENDER_TIMER_PRESENTATION_FEEDBACK { return }
+
+ let next = presentation?.next()
+ info.pointee.vsync_duration = next?.duration ?? -1
+ info.pointee.skipped_vsyncs = next?.skipped ?? -1
+ info.pointee.last_queue_display_time = next?.time ?? -1
+ }
override func displayLinkCallback(_ displayLink: CVDisplayLink,
_ inNow: UnsafePointer<CVTimeStamp>,
@@ -110,7 +116,6 @@ class MacCommon: Common {
_ flagsIn: CVOptionFlags,
_ flagsOut: UnsafeMutablePointer<CVOptionFlags>) -> CVReturn
{
- let frameTimer = mpv?.macOpts.macos_render_timer ?? Int32(RENDER_TIMER_CALLBACK)
let signalSwap = {
self.swapLock.lock()
self.swapTime += 1
@@ -118,13 +123,18 @@ class MacCommon: Common {
self.swapLock.unlock()
}
- if frameTimer != RENDER_TIMER_SYSTEM {
- if let timer = self.timer, frameTimer == RENDER_TIMER_PRECISE {
+ if option.mac.macos_render_timer > RENDER_TIMER_SYSTEM {
+ if let timer = self.timer, option.mac.macos_render_timer == RENDER_TIMER_PRECISE {
timer.scheduleAt(time: inOutputTime.pointee.hostTime, closure: signalSwap)
return kCVReturnSuccess
}
signalSwap()
+ return kCVReturnSuccess
+ }
+
+ if option.mac.macos_render_timer == RENDER_TIMER_PRESENTATION_FEEDBACK {
+ presentation?.add(time: inOutputTime.pointee)
}
return kCVReturnSuccess
@@ -144,27 +154,16 @@ class MacCommon: Common {
flagEvents(VO_EVENT_AMBIENT_LIGHTING_CHANGED)
}
- @objc override func updateICCProfile() {
- guard let colorSpace = window?.screen?.colorSpace else {
- log.sendWarning("Couldn't update ICC Profile, no color space available")
- return
- }
-
- layer?.colorspace = colorSpace.cgColorSpace
+ override func updateICCProfile() {
flagEvents(VO_EVENT_ICC_PROFILE_CHANGED)
}
override func windowDidResize() {
- guard let window = window else {
- log.sendWarning("No window available on window resize event")
- return
- }
-
- updateRenderSize(window.framePixel.size)
+ flagEvents(VO_EVENT_RESIZE | VO_EVENT_EXPOSE)
}
override func windowDidChangeScreenProfile() {
- needsICCUpdate = true
+ updateICCProfile()
}
override func windowDidChangeBackingProperties() {
diff --git a/video/out/opengl/common.c b/video/out/opengl/common.c
index ee26508..e3fc1ba 100644
--- a/video/out/opengl/common.c
+++ b/video/out/opengl/common.c
@@ -398,7 +398,7 @@ static const struct gl_functions gl_functions[] = {
.provides = MPGL_CAP_NESTED_ARRAY,
},
// Swap control, always an OS specific extension
- // The OSX code loads this manually.
+ // The macOS code loads this manually.
{
.extension = "GLX_SGI_swap_control",
.functions = (const struct gl_function[]) {
diff --git a/video/out/opengl/context_drm_egl.c b/video/out/opengl/context_drm_egl.c
index 2db428f..ff4de18 100644
--- a/video/out/opengl/context_drm_egl.c
+++ b/video/out/opengl/context_drm_egl.c
@@ -616,6 +616,10 @@ static bool drm_egl_init(struct ra_ctx *ctx)
xrgb_format = GBM_FORMAT_XBGR8888;
break;
default:
+ if (drm->opts->drm_format != DRM_OPTS_FORMAT_XRGB8888) {
+ MP_VERBOSE(ctx->vo, "Requested format not supported by context, "
+ "falling back to xrgb8888\n");
+ }
argb_format = GBM_FORMAT_ARGB8888;
xrgb_format = GBM_FORMAT_XRGB8888;
break;
diff --git a/video/out/opengl/context_glx.c b/video/out/opengl/context_glx.c
index 4062224..a2a63e1 100644
--- a/video/out/opengl/context_glx.c
+++ b/video/out/opengl/context_glx.c
@@ -25,7 +25,7 @@
#define GLX_CONTEXT_FLAGS_ARB 0x2094
#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
#ifndef __APPLE__
-// These are respectively 0x00000001 and 0x00000002 on OSX
+// These are respectively 0x00000001 and 0x00000002 on macOS
#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001
#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
#endif
diff --git a/video/out/opengl/context_rpi.c b/video/out/opengl/context_rpi.c
deleted file mode 100644
index 0b6babb..0000000
--- a/video/out/opengl/context_rpi.c
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <assert.h>
-#include <stdatomic.h>
-#include <stddef.h>
-
-#include <bcm_host.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include "common/common.h"
-#include "video/out/win_state.h"
-#include "context.h"
-#include "egl_helpers.h"
-
-struct priv {
- struct GL gl;
- DISPMANX_DISPLAY_HANDLE_T display;
- DISPMANX_ELEMENT_HANDLE_T window;
- DISPMANX_UPDATE_HANDLE_T update;
- EGLDisplay egl_display;
- EGLConfig egl_config;
- EGLContext egl_context;
- EGLSurface egl_surface;
- // yep, the API keeps a pointer to it
- EGL_DISPMANX_WINDOW_T egl_window;
- int x, y, w, h;
- double display_fps;
- atomic_int reload_display;
- int win_params[4];
-};
-
-static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1,
- uint32_t param2)
-{
- struct ra_ctx *ctx = callback_data;
- struct priv *p = ctx->priv;
- atomic_store(&p->reload_display, true);
- vo_wakeup(ctx->vo);
-}
-
-static void destroy_dispmanx(struct ra_ctx *ctx)
-{
- struct priv *p = ctx->priv;
-
- if (p->egl_surface) {
- eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT);
- eglDestroySurface(p->egl_display, p->egl_surface);
- p->egl_surface = EGL_NO_SURFACE;
- }
-
- if (p->window)
- vc_dispmanx_element_remove(p->update, p->window);
- p->window = 0;
- if (p->display)
- vc_dispmanx_display_close(p->display);
- p->display = 0;
- if (p->update)
- vc_dispmanx_update_submit_sync(p->update);
- p->update = 0;
-}
-
-static void rpi_uninit(struct ra_ctx *ctx)
-{
- struct priv *p = ctx->priv;
- ra_gl_ctx_uninit(ctx);
-
- vc_tv_unregister_callback_full(tv_callback, ctx);
-
- destroy_dispmanx(ctx);
-
- if (p->egl_context)
- eglDestroyContext(p->egl_display, p->egl_context);
- p->egl_context = EGL_NO_CONTEXT;
- eglReleaseThread();
- p->egl_display = EGL_NO_DISPLAY;
-}
-
-static bool recreate_dispmanx(struct ra_ctx *ctx)
-{
- struct priv *p = ctx->priv;
- int display_nr = 0;
- int layer = 0;
-
- MP_VERBOSE(ctx, "Recreating DISPMANX state...\n");
-
- destroy_dispmanx(ctx);
-
- p->display = vc_dispmanx_display_open(display_nr);
- p->update = vc_dispmanx_update_start(0);
- if (!p->display || !p->update) {
- MP_FATAL(ctx, "Could not get DISPMANX objects.\n");
- goto fail;
- }
-
- uint32_t dispw, disph;
- if (graphics_get_display_size(0, &dispw, &disph) < 0) {
- MP_FATAL(ctx, "Could not get display size.\n");
- goto fail;
- }
- p->w = dispw;
- p->h = disph;
-
- if (ctx->vo->opts->fullscreen) {
- p->x = p->y = 0;
- } else {
- struct vo_win_geometry geo;
- struct mp_rect screenrc = {0, 0, p->w, p->h};
-
- vo_calc_window_geometry(ctx->vo, &screenrc, &geo);
-
- mp_rect_intersection(&geo.win, &screenrc);
-
- p->x = geo.win.x0;
- p->y = geo.win.y0;
- p->w = geo.win.x1 - geo.win.x0;
- p->h = geo.win.y1 - geo.win.y0;
- }
-
- // dispmanx is like a neanderthal version of Wayland - you can add an
- // overlay any place on the screen.
- VC_RECT_T dst = {.x = p->x, .y = p->y, .width = p->w, .height = p->h};
- VC_RECT_T src = {.width = p->w << 16, .height = p->h << 16};
- VC_DISPMANX_ALPHA_T alpha = {
- .flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
- .opacity = 0xFF,
- };
- p->window = vc_dispmanx_element_add(p->update, p->display, layer, &dst, 0,
- &src, DISPMANX_PROTECTION_NONE, &alpha,
- 0, 0);
- if (!p->window) {
- MP_FATAL(ctx, "Could not add DISPMANX element.\n");
- goto fail;
- }
-
- vc_dispmanx_update_submit_sync(p->update);
- p->update = vc_dispmanx_update_start(0);
-
- p->egl_window = (EGL_DISPMANX_WINDOW_T){
- .element = p->window,
- .width = p->w,
- .height = p->h,
- };
- p->egl_surface = eglCreateWindowSurface(p->egl_display, p->egl_config,
- &p->egl_window, NULL);
-
- if (p->egl_surface == EGL_NO_SURFACE) {
- MP_FATAL(ctx, "Could not create EGL surface!\n");
- goto fail;
- }
-
- if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
- p->egl_context))
- {
- MP_FATAL(ctx, "Failed to set context!\n");
- goto fail;
- }
-
- p->display_fps = 0;
- TV_GET_STATE_RESP_T tvstate;
- TV_DISPLAY_STATE_T tvstate_disp;
- if (!vc_tv_get_state(&tvstate) && !vc_tv_get_display_state(&tvstate_disp)) {
- if (tvstate_disp.state & (VC_HDMI_HDMI | VC_HDMI_DVI)) {
- p->display_fps = tvstate_disp.display.hdmi.frame_rate;
-
- HDMI_PROPERTY_PARAM_T param = {
- .property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE,
- };
- if (!vc_tv_hdmi_get_property(&param) &&
- param.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC)
- p->display_fps = p->display_fps / 1.001;
- } else {
- p->display_fps = tvstate_disp.display.sdtv.frame_rate;
- }
- }
-
- p->win_params[0] = display_nr;
- p->win_params[1] = layer;
- p->win_params[2] = p->x;
- p->win_params[3] = p->y;
-
- ctx->vo->dwidth = p->w;
- ctx->vo->dheight = p->h;
- if (ctx->swapchain)
- ra_gl_ctx_resize(ctx->swapchain, p->w, p->h, 0);
-
- ctx->vo->want_redraw = true;
-
- vo_event(ctx->vo, VO_EVENT_WIN_STATE);
- return true;
-
-fail:
- destroy_dispmanx(ctx);
- return false;
-}
-
-static void rpi_swap_buffers(struct ra_ctx *ctx)
-{
- struct priv *p = ctx->priv;
- eglSwapBuffers(p->egl_display, p->egl_surface);
-}
-
-static bool rpi_init(struct ra_ctx *ctx)
-{
- struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
-
- bcm_host_init();
-
- vc_tv_register_callback(tv_callback, ctx);
-
- p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (!eglInitialize(p->egl_display, NULL, NULL)) {
- MP_FATAL(ctx, "EGL failed to initialize.\n");
- goto fail;
- }
-
- if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context, &p->egl_config))
- goto fail;
-
- if (!recreate_dispmanx(ctx))
- goto fail;
-
- mpegl_load_functions(&p->gl, ctx->log);
-
- struct ra_gl_ctx_params params = {
- .swap_buffers = rpi_swap_buffers,
- };
-
- if (!ra_gl_ctx_init(ctx, &p->gl, params))
- goto fail;
-
- ra_add_native_resource(ctx->ra, "MPV_RPI_WINDOW", p->win_params);
-
- ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0);
- return true;
-
-fail:
- rpi_uninit(ctx);
- return false;
-}
-
-static bool rpi_reconfig(struct ra_ctx *ctx)
-{
- return recreate_dispmanx(ctx);
-}
-
-static struct mp_image *take_screenshot(struct ra_ctx *ctx)
-{
- struct priv *p = ctx->priv;
-
- if (!p->display)
- return NULL;
-
- struct mp_image *img = mp_image_alloc(IMGFMT_BGR0, p->w, p->h);
- if (!img)
- return NULL;
-
- DISPMANX_RESOURCE_HANDLE_T resource =
- vc_dispmanx_resource_create(VC_IMAGE_ARGB8888,
- img->w | ((img->w * 4) << 16), img->h,
- &(int32_t){0});
- if (!resource)
- goto fail;
-
- if (vc_dispmanx_snapshot(p->display, resource, 0))
- goto fail;
-
- VC_RECT_T rc = {.width = img->w, .height = img->h};
- if (vc_dispmanx_resource_read_data(resource, &rc, img->planes[0], img->stride[0]))
- goto fail;
-
- vc_dispmanx_resource_delete(resource);
- return img;
-
-fail:
- vc_dispmanx_resource_delete(resource);
- talloc_free(img);
- return NULL;
-}
-
-static int rpi_control(struct ra_ctx *ctx, int *events, int request, void *arg)
-{
- struct priv *p = ctx->priv;
-
- switch (request) {
- case VOCTRL_SCREENSHOT_WIN:
- *(struct mp_image **)arg = take_screenshot(ctx);
- return VO_TRUE;
- case VOCTRL_CHECK_EVENTS:
- if (atomic_fetch_and(&p->reload_display, 0)) {
- MP_WARN(ctx, "Recovering from display mode switch...\n");
- recreate_dispmanx(ctx);
- }
- return VO_TRUE;
- case VOCTRL_GET_DISPLAY_FPS:
- *(double *)arg = p->display_fps;
- return VO_TRUE;
- }
-
- return VO_NOTIMPL;
-}
-
-const struct ra_ctx_fns ra_ctx_rpi = {
- .type = "opengl",
- .name = "rpi",
- .reconfig = rpi_reconfig,
- .control = rpi_control,
- .init = rpi_init,
- .uninit = rpi_uninit,
-};
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index 26c5268..2c5611b 100644
--- a/video/out/opengl/context_wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -47,14 +47,14 @@ static void resize(struct ra_ctx *ctx)
const int32_t width = mp_rect_w(wl->geometry);
const int32_t height = mp_rect_h(wl->geometry);
+ vo_wayland_handle_scale(wl);
+
vo_wayland_set_opaque_region(wl, ctx->opts.want_alpha);
if (p->egl_window)
wl_egl_window_resize(p->egl_window, width, height, 0, 0);
wl->vo->dwidth = width;
wl->vo->dheight = height;
-
- vo_wayland_handle_fractional_scale(wl);
}
static bool wayland_egl_check_visible(struct ra_ctx *ctx)
diff --git a/video/out/opengl/context_win.c b/video/out/opengl/context_win.c
index 968b176..a582e28 100644
--- a/video/out/opengl/context_win.c
+++ b/video/out/opengl/context_win.c
@@ -96,6 +96,7 @@ static bool create_dc(struct ra_ctx *ctx)
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
+ pfd.cAlphaBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
int pf = ChoosePixelFormat(hdc, &pfd);
@@ -293,6 +294,9 @@ static bool wgl_init(struct ra_ctx *ctx)
if (!vo_w32_init(ctx->vo))
goto fail;
+ if (ctx->opts.want_alpha)
+ vo_w32_set_transparency(ctx->vo, ctx->opts.want_alpha);
+
vo_w32_run_on_thread(ctx->vo, create_ctx, ctx);
if (!p->context)
goto fail;
@@ -368,11 +372,17 @@ static int wgl_control(struct ra_ctx *ctx, int *events, int request, void *arg)
return ret;
}
+static void wgl_update_render_opts(struct ra_ctx *ctx)
+{
+ vo_w32_set_transparency(ctx->vo, ctx->opts.want_alpha);
+}
+
const struct ra_ctx_fns ra_ctx_wgl = {
- .type = "opengl",
- .name = "win",
- .init = wgl_init,
- .reconfig = wgl_reconfig,
- .control = wgl_control,
- .uninit = wgl_uninit,
+ .type = "opengl",
+ .name = "win",
+ .init = wgl_init,
+ .reconfig = wgl_reconfig,
+ .control = wgl_control,
+ .update_render_opts = wgl_update_render_opts,
+ .uninit = wgl_uninit,
};
diff --git a/video/out/opengl/egl_helpers.c b/video/out/opengl/egl_helpers.c
index 3bf6239..18d9027 100644
--- a/video/out/opengl/egl_helpers.c
+++ b/video/out/opengl/egl_helpers.c
@@ -192,18 +192,20 @@ static bool create_context(struct ra_ctx *ctx, EGLDisplay display,
}
if (!egl_ctx) {
// Fallback for EGL 1.4 without EGL_KHR_create_context or GLES
- // Add the context flags only for GLES - GL has been attempted above
EGLint attrs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- es ? EGL_CONTEXT_FLAGS_KHR : EGL_NONE, ctx_flags,
+ EGL_CONTEXT_FLAGS_KHR, ctx_flags,
+ es ? EGL_CONTEXT_CLIENT_VERSION : EGL_NONE, 2,
EGL_NONE
};
egl_ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
+ if (!egl_ctx)
+ egl_ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, &attrs[2]);
}
if (!egl_ctx) {
- MP_MSG(ctx, msgl, "Could not create EGL context for %s!\n", name);
+ MP_MSG(ctx, msgl, "Could not create EGL context for %s (error=%d)!\n",
+ name, eglGetError());
return false;
}
diff --git a/video/out/opengl/formats.c b/video/out/opengl/formats.c
index a0b79e2..dd0e89d 100644
--- a/video/out/opengl/formats.c
+++ b/video/out/opengl/formats.c
@@ -94,7 +94,7 @@ const struct gl_format gl_formats[] = {
// Special formats.
{"rgb565", GL_RGB8, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5, F_TF | F_GL2 | F_GL3},
- // Worthless, but needed by OSX videotoolbox interop on old Apple hardware.
+ // Worthless, but needed by macOS videotoolbox interop on old Apple hardware.
{"appleyp", GL_RGB, GL_RGB_422_APPLE,
GL_UNSIGNED_SHORT_8_8_APPLE, F_TF | F_APPL},
diff --git a/video/out/opengl/hwdec_rpi.c b/video/out/opengl/hwdec_rpi.c
deleted file mode 100644
index 5362832..0000000
--- a/video/out/opengl/hwdec_rpi.c
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <stdbool.h>
-#include <assert.h>
-
-#include <bcm_host.h>
-#include <interface/mmal/mmal.h>
-#include <interface/mmal/util/mmal_util.h>
-#include <interface/mmal/util/mmal_default_components.h>
-#include <interface/mmal/vc/mmal_vc_api.h>
-
-#include <libavutil/rational.h>
-
-#include "common/common.h"
-#include "common/msg.h"
-#include "video/mp_image.h"
-#include "video/out/gpu/hwdec.h"
-
-#include "common.h"
-
-struct priv {
- struct mp_log *log;
-
- struct mp_image_params params;
-
- MMAL_COMPONENT_T *renderer;
- bool renderer_enabled;
-
- // for RAM input
- MMAL_POOL_T *swpool;
-
- struct mp_image *current_frame;
-
- struct mp_rect src, dst;
- int cur_window[4]; // raw user params
-};
-
-// Magic alignments (in pixels) expected by the MMAL internals.
-#define ALIGN_W 32
-#define ALIGN_H 16
-
-// Make mpi point to buffer, assuming MMAL_ENCODING_I420.
-// buffer can be NULL.
-// Return the required buffer space.
-static size_t layout_buffer(struct mp_image *mpi, MMAL_BUFFER_HEADER_T *buffer,
- struct mp_image_params *params)
-{
- assert(params->imgfmt == IMGFMT_420P);
- mp_image_set_params(mpi, params);
- int w = MP_ALIGN_UP(params->w, ALIGN_W);
- int h = MP_ALIGN_UP(params->h, ALIGN_H);
- uint8_t *cur = buffer ? buffer->data : NULL;
- size_t size = 0;
- for (int i = 0; i < 3; i++) {
- int div = i ? 2 : 1;
- mpi->planes[i] = cur;
- mpi->stride[i] = w / div;
- size_t plane_size = h / div * mpi->stride[i];
- if (cur)
- cur += plane_size;
- size += plane_size;
- }
- return size;
-}
-
-static MMAL_FOURCC_T map_csp(enum mp_csp csp)
-{
- switch (csp) {
- case MP_CSP_BT_601: return MMAL_COLOR_SPACE_ITUR_BT601;
- case MP_CSP_BT_709: return MMAL_COLOR_SPACE_ITUR_BT709;
- case MP_CSP_SMPTE_240M: return MMAL_COLOR_SPACE_SMPTE240M;
- default: return MMAL_COLOR_SPACE_UNKNOWN;
- }
-}
-
-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
-{
- mmal_buffer_header_release(buffer);
-}
-
-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
-{
- struct mp_image *mpi = buffer->user_data;
- talloc_free(mpi);
-}
-
-static void disable_renderer(struct ra_hwdec *hw)
-{
- struct priv *p = hw->priv;
-
- if (p->renderer_enabled) {
- mmal_port_disable(p->renderer->control);
- mmal_port_disable(p->renderer->input[0]);
-
- mmal_port_flush(p->renderer->control);
- mmal_port_flush(p->renderer->input[0]);
-
- mmal_component_disable(p->renderer);
- }
- mmal_pool_destroy(p->swpool);
- p->swpool = NULL;
- p->renderer_enabled = false;
-}
-
-// check_window_only: assume params and dst/src rc are unchanged
-static void update_overlay(struct ra_hwdec *hw, bool check_window_only)
-{
- struct priv *p = hw->priv;
- MMAL_PORT_T *input = p->renderer->input[0];
- struct mp_rect src = p->src;
- struct mp_rect dst = p->dst;
-
- int defs[4] = {0, 0, 0, 0};
- int *z = ra_get_native_resource(hw->ra_ctx->ra, "MPV_RPI_WINDOW");
- if (!z)
- z = defs;
-
- // As documented in the libmpv openglcb headers.
- int display = z[0];
- int layer = z[1];
- int x = z[2];
- int y = z[3];
-
- if (check_window_only && memcmp(z, p->cur_window, sizeof(p->cur_window)) == 0)
- return;
-
- memcpy(p->cur_window, z, sizeof(p->cur_window));
-
- int rotate[] = {MMAL_DISPLAY_ROT0,
- MMAL_DISPLAY_ROT90,
- MMAL_DISPLAY_ROT180,
- MMAL_DISPLAY_ROT270};
-
- int src_w = src.x1 - src.x0, src_h = src.y1 - src.y0,
- dst_w = dst.x1 - dst.x0, dst_h = dst.y1 - dst.y0;
- int p_x, p_y;
- av_reduce(&p_x, &p_y, dst_w * src_h, src_w * dst_h, 16000);
- MMAL_DISPLAYREGION_T dr = {
- .hdr = { .id = MMAL_PARAMETER_DISPLAYREGION,
- .size = sizeof(MMAL_DISPLAYREGION_T), },
- .src_rect = { .x = src.x0, .y = src.y0,
- .width = src_w, .height = src_h },
- .dest_rect = { .x = dst.x0 + x, .y = dst.y0 + y,
- .width = dst_w, .height = dst_h },
- .layer = layer - 1, // under the GL layer
- .display_num = display,
- .pixel_x = p_x,
- .pixel_y = p_y,
- .transform = rotate[p->params.rotate / 90],
- .fullscreen = 0,
- .set = MMAL_DISPLAY_SET_SRC_RECT | MMAL_DISPLAY_SET_DEST_RECT |
- MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM |
- MMAL_DISPLAY_SET_PIXEL | MMAL_DISPLAY_SET_TRANSFORM |
- MMAL_DISPLAY_SET_FULLSCREEN,
- };
-
- if (p->params.rotate % 180 == 90) {
- MPSWAP(int, dr.src_rect.x, dr.src_rect.y);
- MPSWAP(int, dr.src_rect.width, dr.src_rect.height);
- }
-
- if (mmal_port_parameter_set(input, &dr.hdr))
- MP_WARN(p, "could not set video rectangle\n");
-}
-
-static int enable_renderer(struct ra_hwdec *hw)
-{
- struct priv *p = hw->priv;
- MMAL_PORT_T *input = p->renderer->input[0];
- struct mp_image_params *params = &p->params;
-
- if (p->renderer_enabled)
- return 0;
-
- if (!params->imgfmt)
- return -1;
-
- bool opaque = params->imgfmt == IMGFMT_MMAL;
-
- input->format->encoding = opaque ? MMAL_ENCODING_OPAQUE : MMAL_ENCODING_I420;
- input->format->es->video.width = MP_ALIGN_UP(params->w, ALIGN_W);
- input->format->es->video.height = MP_ALIGN_UP(params->h, ALIGN_H);
- input->format->es->video.crop = (MMAL_RECT_T){0, 0, params->w, params->h};
- input->format->es->video.par = (MMAL_RATIONAL_T){params->p_w, params->p_h};
- input->format->es->video.color_space = map_csp(params->color.space);
-
- if (mmal_port_format_commit(input))
- return -1;
-
- input->buffer_num = MPMAX(input->buffer_num_min,
- input->buffer_num_recommended) + 3;
- input->buffer_size = MPMAX(input->buffer_size_min,
- input->buffer_size_recommended);
-
- if (!opaque) {
- size_t size = layout_buffer(&(struct mp_image){0}, NULL, params);
- if (input->buffer_size != size) {
- MP_FATAL(hw, "We disagree with MMAL about buffer sizes.\n");
- return -1;
- }
-
- p->swpool = mmal_pool_create(input->buffer_num, input->buffer_size);
- if (!p->swpool) {
- MP_FATAL(hw, "Could not allocate buffer pool.\n");
- return -1;
- }
- }
-
- update_overlay(hw, false);
-
- p->renderer_enabled = true;
-
- if (mmal_port_enable(p->renderer->control, control_port_cb))
- return -1;
-
- if (mmal_port_enable(input, input_port_cb))
- return -1;
-
- if (mmal_component_enable(p->renderer)) {
- MP_FATAL(hw, "Failed to enable video renderer.\n");
- return -1;
- }
-
- return 0;
-}
-
-static void free_mmal_buffer(void *arg)
-{
- MMAL_BUFFER_HEADER_T *buffer = arg;
- mmal_buffer_header_release(buffer);
-}
-
-static struct mp_image *upload(struct ra_hwdec *hw, struct mp_image *hw_image)
-{
- struct priv *p = hw->priv;
-
- MMAL_BUFFER_HEADER_T *buffer = mmal_queue_wait(p->swpool->queue);
- if (!buffer) {
- MP_ERR(hw, "Can't allocate buffer.\n");
- return NULL;
- }
- mmal_buffer_header_reset(buffer);
-
- struct mp_image *new_ref = mp_image_new_custom_ref(NULL, buffer,
- free_mmal_buffer);
- if (!new_ref) {
- mmal_buffer_header_release(buffer);
- MP_ERR(hw, "Out of memory.\n");
- return NULL;
- }
-
- mp_image_setfmt(new_ref, IMGFMT_MMAL);
- new_ref->planes[3] = (void *)buffer;
-
- struct mp_image dmpi = {0};
- buffer->length = layout_buffer(&dmpi, buffer, &p->params);
- mp_image_copy(&dmpi, hw_image);
-
- return new_ref;
-}
-
-static int overlay_frame(struct ra_hwdec *hw, struct mp_image *hw_image,
- struct mp_rect *src, struct mp_rect *dst, bool newframe)
-{
- struct priv *p = hw->priv;
-
- if (hw_image && !mp_image_params_equal(&p->params, &hw_image->params)) {
- p->params = hw_image->params;
-
- disable_renderer(hw);
- mp_image_unrefp(&p->current_frame);
-
- if (enable_renderer(hw) < 0)
- return -1;
- }
-
- if (hw_image && p->current_frame && !newframe) {
- if (!mp_rect_equals(&p->src, src) ||mp_rect_equals(&p->dst, dst)) {
- p->src = *src;
- p->dst = *dst;
- update_overlay(hw, false);
- }
- return 0; // don't reupload
- }
-
- mp_image_unrefp(&p->current_frame);
-
- if (!hw_image) {
- disable_renderer(hw);
- return 0;
- }
-
- if (enable_renderer(hw) < 0)
- return -1;
-
- update_overlay(hw, true);
-
- struct mp_image *mpi = NULL;
- if (hw_image->imgfmt == IMGFMT_MMAL) {
- mpi = mp_image_new_ref(hw_image);
- } else {
- mpi = upload(hw, hw_image);
- }
-
- if (!mpi) {
- disable_renderer(hw);
- return -1;
- }
-
- MMAL_BUFFER_HEADER_T *ref = (void *)mpi->planes[3];
-
- // Assume this field is free for use by us.
- ref->user_data = mpi;
-
- if (mmal_port_send_buffer(p->renderer->input[0], ref)) {
- MP_ERR(hw, "could not queue picture!\n");
- talloc_free(mpi);
- return -1;
- }
-
- return 0;
-}
-
-static void destroy(struct ra_hwdec *hw)
-{
- struct priv *p = hw->priv;
-
- disable_renderer(hw);
-
- if (p->renderer)
- mmal_component_release(p->renderer);
-
- mmal_vc_deinit();
-}
-
-static int create(struct ra_hwdec *hw)
-{
- struct priv *p = hw->priv;
- p->log = hw->log;
-
- bcm_host_init();
-
- if (mmal_vc_init()) {
- MP_FATAL(hw, "Could not initialize MMAL.\n");
- return -1;
- }
-
- if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &p->renderer))
- {
- MP_FATAL(hw, "Could not create MMAL renderer.\n");
- mmal_vc_deinit();
- return -1;
- }
-
- return 0;
-}
-
-const struct ra_hwdec_driver ra_hwdec_rpi_overlay = {
- .name = "rpi-overlay",
- .priv_size = sizeof(struct priv),
- .imgfmts = {IMGFMT_MMAL, IMGFMT_420P, 0},
- .init = create,
- .overlay_frame = overlay_frame,
- .uninit = destroy,
-};
diff --git a/video/out/placebo/ra_pl.c b/video/out/placebo/ra_pl.c
index 6259651..14c9444 100644
--- a/video/out/placebo/ra_pl.c
+++ b/video/out/placebo/ra_pl.c
@@ -534,7 +534,7 @@ static void renderpass_run_pl(struct ra *ra,
.data = val->data,
});
} else {
- struct pl_desc_binding bind;
+ struct pl_desc_binding bind = {0};
switch (inp->type) {
case RA_VARTYPE_TEX:
case RA_VARTYPE_IMG_W: {
diff --git a/video/out/placebo/utils.c b/video/out/placebo/utils.c
index 1209b72..dae9b14 100644
--- a/video/out/placebo/utils.c
+++ b/video/out/placebo/utils.c
@@ -25,7 +25,7 @@ static const enum pl_log_level msg_lev_to_pl_log[MSGL_MAX+1] = {
};
// translates log levels while probing
-static const enum pl_log_level probing_map(enum pl_log_level level)
+static enum pl_log_level probing_map(enum pl_log_level level)
{
switch (level) {
case PL_LOG_FATAL:
@@ -44,6 +44,12 @@ static void log_cb(void *priv, enum pl_log_level level, const char *msg)
mp_msg(log, pl_log_to_msg_lev[level], "%s\n", msg);
}
+static int determine_pl_log_level(struct mp_log *log)
+{
+ int log_level = mp_msg_level(log);
+ return log_level == -1 ? PL_LOG_NONE : msg_lev_to_pl_log[log_level];
+}
+
static void log_cb_probing(void *priv, enum pl_log_level level, const char *msg)
{
struct mp_log *log = priv;
@@ -54,7 +60,7 @@ pl_log mppl_log_create(void *tactx, struct mp_log *log)
{
return pl_log_create(PL_API_VER, &(struct pl_log_params) {
.log_cb = log_cb,
- .log_level = msg_lev_to_pl_log[mp_msg_level(log)],
+ .log_level = determine_pl_log_level(log),
.log_priv = mp_log_new(tactx, log, "libplacebo"),
});
}
@@ -65,199 +71,3 @@ void mppl_log_set_probing(pl_log log, bool probing)
params.log_cb = probing ? log_cb_probing : log_cb;
pl_log_update(log, &params);
}
-
-enum pl_color_primaries mp_prim_to_pl(enum mp_csp_prim prim)
-{
- switch (prim) {
- case MP_CSP_PRIM_AUTO: return PL_COLOR_PRIM_UNKNOWN;
- case MP_CSP_PRIM_BT_601_525: return PL_COLOR_PRIM_BT_601_525;
- case MP_CSP_PRIM_BT_601_625: return PL_COLOR_PRIM_BT_601_625;
- case MP_CSP_PRIM_BT_709: return PL_COLOR_PRIM_BT_709;
- case MP_CSP_PRIM_BT_2020: return PL_COLOR_PRIM_BT_2020;
- case MP_CSP_PRIM_BT_470M: return PL_COLOR_PRIM_BT_470M;
- case MP_CSP_PRIM_APPLE: return PL_COLOR_PRIM_APPLE;
- case MP_CSP_PRIM_ADOBE: return PL_COLOR_PRIM_ADOBE;
- case MP_CSP_PRIM_PRO_PHOTO: return PL_COLOR_PRIM_PRO_PHOTO;
- case MP_CSP_PRIM_CIE_1931: return PL_COLOR_PRIM_CIE_1931;
- case MP_CSP_PRIM_DCI_P3: return PL_COLOR_PRIM_DCI_P3;
- case MP_CSP_PRIM_DISPLAY_P3: return PL_COLOR_PRIM_DISPLAY_P3;
- case MP_CSP_PRIM_V_GAMUT: return PL_COLOR_PRIM_V_GAMUT;
- case MP_CSP_PRIM_S_GAMUT: return PL_COLOR_PRIM_S_GAMUT;
- case MP_CSP_PRIM_EBU_3213: return PL_COLOR_PRIM_EBU_3213;
- case MP_CSP_PRIM_FILM_C: return PL_COLOR_PRIM_FILM_C;
- case MP_CSP_PRIM_ACES_AP0: return PL_COLOR_PRIM_ACES_AP0;
- case MP_CSP_PRIM_ACES_AP1: return PL_COLOR_PRIM_ACES_AP1;
- case MP_CSP_PRIM_COUNT: return PL_COLOR_PRIM_COUNT;
- }
-
- MP_ASSERT_UNREACHABLE();
-}
-
-enum mp_csp_prim mp_prim_from_pl(enum pl_color_primaries prim)
-{
- switch (prim){
- case PL_COLOR_PRIM_UNKNOWN: return MP_CSP_PRIM_AUTO;
- case PL_COLOR_PRIM_BT_601_525: return MP_CSP_PRIM_BT_601_525;
- case PL_COLOR_PRIM_BT_601_625: return MP_CSP_PRIM_BT_601_625;
- case PL_COLOR_PRIM_BT_709: return MP_CSP_PRIM_BT_709;
- case PL_COLOR_PRIM_BT_2020: return MP_CSP_PRIM_BT_2020;
- case PL_COLOR_PRIM_BT_470M: return MP_CSP_PRIM_BT_470M;
- case PL_COLOR_PRIM_APPLE: return MP_CSP_PRIM_APPLE;
- case PL_COLOR_PRIM_ADOBE: return MP_CSP_PRIM_ADOBE;
- case PL_COLOR_PRIM_PRO_PHOTO: return MP_CSP_PRIM_PRO_PHOTO;
- case PL_COLOR_PRIM_CIE_1931: return MP_CSP_PRIM_CIE_1931;
- case PL_COLOR_PRIM_DCI_P3: return MP_CSP_PRIM_DCI_P3;
- case PL_COLOR_PRIM_DISPLAY_P3: return MP_CSP_PRIM_DISPLAY_P3;
- case PL_COLOR_PRIM_V_GAMUT: return MP_CSP_PRIM_V_GAMUT;
- case PL_COLOR_PRIM_S_GAMUT: return MP_CSP_PRIM_S_GAMUT;
- case PL_COLOR_PRIM_EBU_3213: return MP_CSP_PRIM_EBU_3213;
- case PL_COLOR_PRIM_FILM_C: return MP_CSP_PRIM_FILM_C;
- case PL_COLOR_PRIM_ACES_AP0: return MP_CSP_PRIM_ACES_AP0;
- case PL_COLOR_PRIM_ACES_AP1: return MP_CSP_PRIM_ACES_AP1;
- case PL_COLOR_PRIM_COUNT: return MP_CSP_PRIM_COUNT;
- }
-
- MP_ASSERT_UNREACHABLE();
-}
-
-enum pl_color_transfer mp_trc_to_pl(enum mp_csp_trc trc)
-{
- switch (trc) {
- case MP_CSP_TRC_AUTO: return PL_COLOR_TRC_UNKNOWN;
- case MP_CSP_TRC_BT_1886: return PL_COLOR_TRC_BT_1886;
- case MP_CSP_TRC_SRGB: return PL_COLOR_TRC_SRGB;
- case MP_CSP_TRC_LINEAR: return PL_COLOR_TRC_LINEAR;
- case MP_CSP_TRC_GAMMA18: return PL_COLOR_TRC_GAMMA18;
- case MP_CSP_TRC_GAMMA20: return PL_COLOR_TRC_GAMMA20;
- case MP_CSP_TRC_GAMMA22: return PL_COLOR_TRC_GAMMA22;
- case MP_CSP_TRC_GAMMA24: return PL_COLOR_TRC_GAMMA24;
- case MP_CSP_TRC_GAMMA26: return PL_COLOR_TRC_GAMMA26;
- case MP_CSP_TRC_GAMMA28: return PL_COLOR_TRC_GAMMA28;
- case MP_CSP_TRC_PRO_PHOTO: return PL_COLOR_TRC_PRO_PHOTO;
- case MP_CSP_TRC_PQ: return PL_COLOR_TRC_PQ;
- case MP_CSP_TRC_HLG: return PL_COLOR_TRC_HLG;
- case MP_CSP_TRC_V_LOG: return PL_COLOR_TRC_V_LOG;
- case MP_CSP_TRC_S_LOG1: return PL_COLOR_TRC_S_LOG1;
- case MP_CSP_TRC_S_LOG2: return PL_COLOR_TRC_S_LOG2;
- case MP_CSP_TRC_ST428: return PL_COLOR_TRC_ST428;
- case MP_CSP_TRC_COUNT: return PL_COLOR_TRC_COUNT;
- }
-
- MP_ASSERT_UNREACHABLE();
-}
-
-enum mp_csp_trc mp_trc_from_pl(enum pl_color_transfer trc)
-{
- switch (trc){
- case PL_COLOR_TRC_UNKNOWN: return MP_CSP_TRC_AUTO;
- case PL_COLOR_TRC_BT_1886: return MP_CSP_TRC_BT_1886;
- case PL_COLOR_TRC_SRGB: return MP_CSP_TRC_SRGB;
- case PL_COLOR_TRC_LINEAR: return MP_CSP_TRC_LINEAR;
- case PL_COLOR_TRC_GAMMA18: return MP_CSP_TRC_GAMMA18;
- case PL_COLOR_TRC_GAMMA20: return MP_CSP_TRC_GAMMA20;
- case PL_COLOR_TRC_GAMMA22: return MP_CSP_TRC_GAMMA22;
- case PL_COLOR_TRC_GAMMA24: return MP_CSP_TRC_GAMMA24;
- case PL_COLOR_TRC_GAMMA26: return MP_CSP_TRC_GAMMA26;
- case PL_COLOR_TRC_GAMMA28: return MP_CSP_TRC_GAMMA28;
- case PL_COLOR_TRC_PRO_PHOTO: return MP_CSP_TRC_PRO_PHOTO;
- case PL_COLOR_TRC_PQ: return MP_CSP_TRC_PQ;
- case PL_COLOR_TRC_HLG: return MP_CSP_TRC_HLG;
- case PL_COLOR_TRC_V_LOG: return MP_CSP_TRC_V_LOG;
- case PL_COLOR_TRC_S_LOG1: return MP_CSP_TRC_S_LOG1;
- case PL_COLOR_TRC_S_LOG2: return MP_CSP_TRC_S_LOG2;
- case PL_COLOR_TRC_ST428: return MP_CSP_TRC_ST428;
- case PL_COLOR_TRC_COUNT: return MP_CSP_TRC_COUNT;
- }
-
- MP_ASSERT_UNREACHABLE();
-}
-
-enum pl_color_system mp_csp_to_pl(enum mp_csp csp)
-{
- switch (csp) {
- case MP_CSP_AUTO: return PL_COLOR_SYSTEM_UNKNOWN;
- case MP_CSP_BT_601: return PL_COLOR_SYSTEM_BT_601;
- case MP_CSP_BT_709: return PL_COLOR_SYSTEM_BT_709;
- case MP_CSP_SMPTE_240M: return PL_COLOR_SYSTEM_SMPTE_240M;
- case MP_CSP_BT_2020_NC: return PL_COLOR_SYSTEM_BT_2020_NC;
- case MP_CSP_BT_2020_C: return PL_COLOR_SYSTEM_BT_2020_C;
- case MP_CSP_RGB: return PL_COLOR_SYSTEM_RGB;
- case MP_CSP_XYZ: return PL_COLOR_SYSTEM_XYZ;
- case MP_CSP_YCGCO: return PL_COLOR_SYSTEM_YCGCO;
- case MP_CSP_COUNT: return PL_COLOR_SYSTEM_COUNT;
- }
-
- MP_ASSERT_UNREACHABLE();
-}
-
-enum pl_color_levels mp_levels_to_pl(enum mp_csp_levels levels)
-{
- switch (levels) {
- case MP_CSP_LEVELS_AUTO: return PL_COLOR_LEVELS_UNKNOWN;
- case MP_CSP_LEVELS_TV: return PL_COLOR_LEVELS_TV;
- case MP_CSP_LEVELS_PC: return PL_COLOR_LEVELS_PC;
- case MP_CSP_LEVELS_COUNT: return PL_COLOR_LEVELS_COUNT;
- }
-
- MP_ASSERT_UNREACHABLE();
-}
-
-enum mp_csp_levels mp_levels_from_pl(enum pl_color_levels levels)
-{
- switch (levels){
- case PL_COLOR_LEVELS_UNKNOWN: return MP_CSP_LEVELS_AUTO;
- case PL_COLOR_LEVELS_TV: return MP_CSP_LEVELS_TV;
- case PL_COLOR_LEVELS_PC: return MP_CSP_LEVELS_PC;
- case PL_COLOR_LEVELS_COUNT: return MP_CSP_LEVELS_COUNT;
- }
-
- MP_ASSERT_UNREACHABLE();
-}
-
-enum pl_alpha_mode mp_alpha_to_pl(enum mp_alpha_type alpha)
-{
- switch (alpha) {
- case MP_ALPHA_AUTO: return PL_ALPHA_UNKNOWN;
- case MP_ALPHA_STRAIGHT: return PL_ALPHA_INDEPENDENT;
- case MP_ALPHA_PREMUL: return PL_ALPHA_PREMULTIPLIED;
- }
-
- MP_ASSERT_UNREACHABLE();
-}
-
-enum pl_chroma_location mp_chroma_to_pl(enum mp_chroma_location chroma)
-{
- switch (chroma) {
- case MP_CHROMA_AUTO: return PL_CHROMA_UNKNOWN;
- case MP_CHROMA_TOPLEFT: return PL_CHROMA_TOP_LEFT;
- case MP_CHROMA_LEFT: return PL_CHROMA_LEFT;
- case MP_CHROMA_CENTER: return PL_CHROMA_CENTER;
- case MP_CHROMA_COUNT: return PL_CHROMA_COUNT;
- }
-
- MP_ASSERT_UNREACHABLE();
-}
-
-void mp_map_dovi_metadata_to_pl(struct mp_image *mpi,
- struct pl_frame *frame)
-{
-#ifdef PL_HAVE_LAV_DOLBY_VISION
- if (mpi->dovi) {
- const AVDOVIMetadata *metadata = (AVDOVIMetadata *) mpi->dovi->data;
- const AVDOVIRpuDataHeader *header = av_dovi_get_header(metadata);
-
- if (header->disable_residual_flag) {
- // Only automatically map DoVi RPUs that don't require an EL
- struct pl_dovi_metadata *dovi = talloc_ptrtype(mpi, dovi);
- pl_frame_map_avdovi_metadata(frame, dovi, metadata);
- }
- }
-
-#if defined(PL_HAVE_LIBDOVI)
- if (mpi->dovi_buf)
- pl_hdr_metadata_from_dovi_rpu(&frame->color.hdr, mpi->dovi_buf->data,
- mpi->dovi_buf->size);
-#endif
-
-#endif // PL_HAVE_LAV_DOLBY_VISION
-}
diff --git a/video/out/placebo/utils.h b/video/out/placebo/utils.h
index bf780a8..3f61d8b 100644
--- a/video/out/placebo/utils.h
+++ b/video/out/placebo/utils.h
@@ -26,16 +26,3 @@ static inline struct pl_rect2d mp_rect2d_to_pl(struct mp_rect rc)
.y1 = rc.y1,
};
}
-
-enum pl_color_primaries mp_prim_to_pl(enum mp_csp_prim prim);
-enum mp_csp_prim mp_prim_from_pl(enum pl_color_primaries prim);
-enum pl_color_transfer mp_trc_to_pl(enum mp_csp_trc trc);
-enum mp_csp_trc mp_trc_from_pl(enum pl_color_transfer trc);
-enum pl_color_system mp_csp_to_pl(enum mp_csp csp);
-enum pl_color_levels mp_levels_to_pl(enum mp_csp_levels levels);
-enum mp_csp_levels mp_levels_from_pl(enum pl_color_levels levels);
-enum pl_alpha_mode mp_alpha_to_pl(enum mp_alpha_type alpha);
-enum pl_chroma_location mp_chroma_to_pl(enum mp_chroma_location chroma);
-
-void mp_map_dovi_metadata_to_pl(struct mp_image *mpi,
- struct pl_frame *frame);
diff --git a/video/out/vo.c b/video/out/vo.c
index 50129fb..db29690 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -63,14 +63,12 @@ extern const struct vo_driver video_out_sdl;
extern const struct vo_driver video_out_vaapi;
extern const struct vo_driver video_out_dmabuf_wayland;
extern const struct vo_driver video_out_wlshm;
-extern const struct vo_driver video_out_rpi;
extern const struct vo_driver video_out_tct;
extern const struct vo_driver video_out_sixel;
extern const struct vo_driver video_out_kitty;
static const struct vo_driver *const video_out_drivers[] =
{
- &video_out_libmpv,
#if HAVE_ANDROID
&video_out_mediacodec_embed,
#endif
@@ -100,6 +98,7 @@ static const struct vo_driver *const video_out_drivers[] =
#if HAVE_X11
&video_out_x11,
#endif
+ &video_out_libmpv,
&video_out_null,
// should not be auto-selected
&video_out_image,
@@ -110,9 +109,6 @@ static const struct vo_driver *const video_out_drivers[] =
#if HAVE_DRM
&video_out_drm,
#endif
-#if HAVE_RPI_MMAL
- &video_out_rpi,
-#endif
#if HAVE_SIXEL
&video_out_sixel,
#endif
@@ -138,6 +134,7 @@ struct vo_internal {
bool want_redraw; // redraw request from VO to player
bool send_reset; // send VOCTRL_RESET
bool paused;
+ bool wakeup_on_done;
int queued_events; // event mask for the user
int internal_events; // event mask for us
@@ -239,7 +236,6 @@ static void update_opts(void *p)
if (m_config_cache_update(vo->opts_cache)) {
read_opts(vo);
-
if (vo->driver->control) {
vo->driver->control(vo, VOCTRL_VO_OPTS_CHANGED, NULL);
// "Legacy" update of video position related options.
@@ -247,18 +243,6 @@ static void update_opts(void *p)
vo->driver->control(vo, VOCTRL_SET_PANSCAN, NULL);
}
}
-
- if (vo->gl_opts_cache && m_config_cache_update(vo->gl_opts_cache)) {
- // "Legacy" update of video GL renderer related options.
- if (vo->driver->control)
- vo->driver->control(vo, VOCTRL_UPDATE_RENDER_OPTS, NULL);
- }
-
- if (m_config_cache_update(vo->eq_opts_cache)) {
- // "Legacy" update of video equalizer related options.
- if (vo->driver->control)
- vo->driver->control(vo, VOCTRL_SET_EQUALIZER, NULL);
- }
}
// Does not include thread- and VO uninit.
@@ -270,6 +254,7 @@ static void dealloc_vo(struct vo *vo)
talloc_free(vo->opts_cache);
talloc_free(vo->gl_opts_cache);
talloc_free(vo->eq_opts_cache);
+ mp_mutex_destroy(&vo->params_mutex);
mp_mutex_destroy(&vo->in->lock);
mp_cond_destroy(&vo->in->wakeup);
@@ -301,6 +286,7 @@ static struct vo *vo_create(bool probing, struct mpv_global *global,
.probing = probing,
.in = talloc(vo, struct vo_internal),
};
+ mp_mutex_init(&vo->params_mutex);
talloc_steal(vo, log);
*vo->in = (struct vo_internal) {
.dispatch = mp_dispatch_create(vo),
@@ -319,12 +305,7 @@ static struct vo *vo_create(bool probing, struct mpv_global *global,
update_opts, vo);
vo->gl_opts_cache = m_config_cache_alloc(NULL, global, &gl_video_conf);
- m_config_cache_set_dispatch_change_cb(vo->gl_opts_cache, vo->in->dispatch,
- update_opts, vo);
-
vo->eq_opts_cache = m_config_cache_alloc(NULL, global, &mp_csp_equalizer_conf);
- m_config_cache_set_dispatch_change_cb(vo->eq_opts_cache, vo->in->dispatch,
- update_opts, vo);
mp_input_set_mouse_transform(vo->input_ctx, NULL, NULL);
if (vo->driver->encode != !!vo->encode_lavc_ctx)
@@ -613,8 +594,10 @@ static void run_reconfig(void *p)
mp_image_params_get_dsize(params, &vo->dwidth, &vo->dheight);
+ mp_mutex_lock(&vo->params_mutex);
talloc_free(vo->params);
vo->params = talloc_dup(vo, params);
+ mp_mutex_unlock(&vo->params_mutex);
if (vo->driver->reconfig2) {
*ret = vo->driver->reconfig2(vo, img);
@@ -625,8 +608,10 @@ static void run_reconfig(void *p)
if (vo->config_ok) {
check_vo_caps(vo);
} else {
+ mp_mutex_lock(&vo->params_mutex);
talloc_free(vo->params);
vo->params = NULL;
+ mp_mutex_unlock(&vo->params_mutex);
}
mp_mutex_lock(&in->lock);
@@ -765,6 +750,52 @@ void vo_wakeup(struct vo *vo)
mp_mutex_unlock(&in->lock);
}
+static int64_t get_current_frame_end(struct vo *vo)
+{
+ struct vo_internal *in = vo->in;
+ if (!in->current_frame)
+ return -1;
+ return in->current_frame->pts + MPMAX(in->current_frame->duration, 0);
+}
+
+static bool still_displaying(struct vo *vo)
+{
+ struct vo_internal *in = vo->in;
+ bool working = in->rendering || in->frame_queued;
+ if (working)
+ goto done;
+
+ int64_t frame_end = get_current_frame_end(vo);
+ if (frame_end < 0)
+ goto done;
+ working = mp_time_ns() < frame_end;
+
+done:
+ return working && in->hasframe;
+}
+
+// Return true if there is still a frame being displayed (or queued).
+bool vo_still_displaying(struct vo *vo)
+{
+ mp_mutex_lock(&vo->in->lock);
+ bool res = still_displaying(vo);
+ mp_mutex_unlock(&vo->in->lock);
+ return res;
+}
+
+// Make vo issue a wakeup once vo_still_displaying() becomes false.
+void vo_request_wakeup_on_done(struct vo *vo)
+{
+ struct vo_internal *in = vo->in;
+ mp_mutex_lock(&vo->in->lock);
+ if (still_displaying(vo)) {
+ in->wakeup_on_done = true;
+ } else {
+ wakeup_core(vo);
+ }
+ mp_mutex_unlock(&vo->in->lock);
+}
+
// Whether vo_queue_frame() can be called. If the VO is not ready yet, the
// function will return false, and the VO will call the wakeup callback once
// it's ready.
@@ -923,6 +954,7 @@ static bool render_frame(struct vo *vo)
if (in->dropped_frame) {
in->drop_count += 1;
+ wakeup_core(vo);
} else {
in->rendering = true;
in->hasframe_rendered = true;
@@ -994,10 +1026,9 @@ static bool render_frame(struct vo *vo)
more_frames = true;
mp_cond_broadcast(&in->wakeup); // for vo_wait_frame()
- wakeup_core(vo);
done:
- if (!vo->driver->frame_owner)
+ if (!vo->driver->frame_owner || in->dropped_frame)
talloc_free(frame);
mp_mutex_unlock(&in->lock);
@@ -1074,6 +1105,8 @@ static MP_THREAD_VOID vo_thread(void *ptr)
bool working = render_frame(vo);
int64_t now = mp_time_ns();
int64_t wait_until = now + MP_TIME_S_TO_NS(working ? 0 : 1000);
+ bool wakeup_on_done = false;
+ int64_t wakeup_core_after = 0;
mp_mutex_lock(&in->lock);
if (in->wakeup_pts) {
@@ -1088,6 +1121,14 @@ static MP_THREAD_VOID vo_thread(void *ptr)
in->want_redraw = true;
wakeup_core(vo);
}
+ if ((!working && !in->rendering && !in->frame_queued) && in->wakeup_on_done) {
+ // At this point we know VO is going to sleep
+ int64_t frame_end = get_current_frame_end(vo);
+ if (frame_end >= 0)
+ wakeup_core_after = frame_end;
+ wakeup_on_done = true;
+ in->wakeup_on_done = false;
+ }
vo->want_redraw = false;
bool redraw = in->request_redraw;
bool send_reset = in->send_reset;
@@ -1110,6 +1151,17 @@ static MP_THREAD_VOID vo_thread(void *ptr)
if (wait_until <= now)
continue;
+ if (wakeup_on_done) {
+ // At this point wait_until should be longer than frame duration
+ if (wakeup_core_after >= 0 && wait_until >= wakeup_core_after) {
+ wait_vo(vo, wakeup_core_after);
+ mp_mutex_lock(&in->lock);
+ in->need_wakeup = true;
+ mp_mutex_unlock(&in->lock);
+ }
+ wakeup_core(vo);
+ }
+
wait_vo(vo, wait_until);
}
forget_frames(vo); // implicitly synchronized
@@ -1185,17 +1237,6 @@ void vo_seek_reset(struct vo *vo)
mp_mutex_unlock(&in->lock);
}
-// Return true if there is still a frame being displayed (or queued).
-// If this returns true, a wakeup some time in the future is guaranteed.
-bool vo_still_displaying(struct vo *vo)
-{
- struct vo_internal *in = vo->in;
- mp_mutex_lock(&in->lock);
- bool working = in->rendering || in->frame_queued;
- mp_mutex_unlock(&in->lock);
- return working && in->hasframe;
-}
-
// Whether at least 1 frame was queued or rendered since last seek or reconfig.
bool vo_has_frame(struct vo *vo)
{
@@ -1433,9 +1474,19 @@ int lookup_keymap_table(const struct mp_keymap *map, int key)
struct mp_image_params vo_get_current_params(struct vo *vo)
{
struct mp_image_params p = {0};
- mp_mutex_lock(&vo->in->lock);
+ mp_mutex_lock(&vo->params_mutex);
if (vo->params)
p = *vo->params;
- mp_mutex_unlock(&vo->in->lock);
+ mp_mutex_unlock(&vo->params_mutex);
+ return p;
+}
+
+struct mp_image_params vo_get_target_params(struct vo *vo)
+{
+ struct mp_image_params p = {0};
+ mp_mutex_lock(&vo->params_mutex);
+ if (vo->target_params)
+ p = *vo->target_params;
+ mp_mutex_unlock(&vo->params_mutex);
return p;
}
diff --git a/video/out/vo.h b/video/out/vo.h
index e38dcf8..313c08a 100644
--- a/video/out/vo.h
+++ b/video/out/vo.h
@@ -29,6 +29,7 @@
#include "video/img_format.h"
#include "common/common.h"
#include "options/options.h"
+#include "osdep/threads.h"
enum {
// VO needs to redraw
@@ -66,7 +67,6 @@ enum mp_voctrl {
VOCTRL_RESUME,
VOCTRL_SET_PANSCAN,
- VOCTRL_SET_EQUALIZER,
// Triggered by any change to mp_vo_opts. This is for convenience. In theory,
// you could install your own listener.
@@ -122,6 +122,13 @@ enum mp_voctrl {
/* private to vo_gpu and vo_gpu_next */
VOCTRL_EXTERNAL_RESIZE,
+
+ // Begin VO dragging.
+ VOCTRL_BEGIN_DRAGGING,
+
+ // Native context menu
+ VOCTRL_SHOW_MENU,
+ VOCTRL_UPDATE_MENU,
};
// Helper to expose what kind of content is currently playing to the VO.
@@ -210,7 +217,7 @@ struct vo_frame {
// If 0, present immediately.
int64_t pts;
// Approximate frame duration, in ns.
- int duration;
+ double duration;
// Realtime of estimated distance between 2 vsync events.
double vsync_interval;
// "ideal" display time within the vsync
@@ -470,7 +477,17 @@ struct vo {
// be accessed unsynchronized (read-only).
int config_ok; // Last config call was successful?
- struct mp_image_params *params; // Configured parameters (as in vo_reconfig)
+
+ // --- The following fields are synchronized by params_mutex, most of
+ // the params are set only in the vo_reconfig and safe to read
+ // unsynchronized. Some of the parameters are updated in draw_frame,
+ // which are still safe to read in the play loop, but for correctness
+ // generic getter is protected by params_mutex.
+ mp_mutex params_mutex;
+ // Configured parameters (changed in vo_reconfig)
+ struct mp_image_params *params;
+ // Target display parameters (VO is responsible for re-/setting)
+ struct mp_image_params *target_params;
// --- The following fields can be accessed only by the VO thread, or from
// anywhere _if_ the VO thread is suspended (use vo->dispatch).
@@ -486,6 +503,9 @@ struct vo {
int dwidth;
int dheight;
float monitor_par;
+
+ // current GPU context (--vo=gpu and --vo=gpu-next only)
+ const char *context_name;
};
struct mpv_global;
@@ -499,6 +519,7 @@ bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts);
void vo_queue_frame(struct vo *vo, struct vo_frame *frame);
void vo_wait_frame(struct vo *vo);
bool vo_still_displaying(struct vo *vo);
+void vo_request_wakeup_on_done(struct vo *vo);
bool vo_has_frame(struct vo *vo);
void vo_redraw(struct vo *vo);
bool vo_want_redraw(struct vo *vo);
@@ -540,5 +561,6 @@ void vo_get_src_dst_rects(struct vo *vo, struct mp_rect *out_src,
struct vo_frame *vo_frame_ref(struct vo_frame *frame);
struct mp_image_params vo_get_current_params(struct vo *vo);
+struct mp_image_params vo_get_target_params(struct vo *vo);
#endif /* MPLAYER_VIDEO_OUT_H */
diff --git a/video/out/vo_direct3d.c b/video/out/vo_direct3d.c
index 16936bb..91e962f 100644
--- a/video/out/vo_direct3d.c
+++ b/video/out/vo_direct3d.c
@@ -102,6 +102,7 @@ typedef struct d3d_priv {
struct mp_osd_res osd_res;
int image_format; /**< mplayer image format */
struct mp_image_params params;
+ struct mp_image_params dst_params;
D3DFORMAT movie_src_fmt; /**< Movie colorspace format (depends on
the movie's codec) */
@@ -896,6 +897,18 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
if (!resize_d3d(priv))
return VO_ERROR;
+ priv->dst_params = *params;
+ for (const struct fmt_entry *cur = &fmt_table[0]; cur->mplayer_fmt; ++cur) {
+ if (cur->fourcc == priv->desktop_fmt) {
+ priv->dst_params.imgfmt = cur->mplayer_fmt;
+ break;
+ }
+ }
+ mp_image_params_guess_csp(&priv->dst_params);
+ mp_mutex_lock(&vo->params_mutex);
+ vo->target_params = &priv->dst_params;
+ mp_mutex_unlock(&vo->params_mutex);
+
return 0; /* Success */
}
diff --git a/video/out/vo_dmabuf_wayland.c b/video/out/vo_dmabuf_wayland.c
index e04ff5d..35a4dac 100644
--- a/video/out/vo_dmabuf_wayland.c
+++ b/video/out/vo_dmabuf_wayland.c
@@ -137,10 +137,10 @@ static const struct wl_buffer_listener osd_buffer_listener = {
};
#if HAVE_VAAPI
-static void close_file_descriptors(VADRMPRIMESurfaceDescriptor desc)
+static void close_file_descriptors(const VADRMPRIMESurfaceDescriptor *desc)
{
- for (int i = 0; i < desc.num_objects; i++)
- close(desc.objects[i].fd);
+ for (int i = 0; i < desc->num_objects; i++)
+ close(desc->objects[i].fd);
}
#endif
@@ -175,7 +175,7 @@ static bool vaapi_drm_format(struct vo *vo, struct mp_image *src)
p->drm_modifier = desc.objects[0].drm_format_modifier;
format = true;
done:
- close_file_descriptors(desc);
+ close_file_descriptors(&desc);
#endif
return format;
}
@@ -216,7 +216,7 @@ static void vaapi_dmabuf_importer(struct buffer *buf, struct mp_image *src,
}
done:
- close_file_descriptors(desc);
+ close_file_descriptors(&desc);
#endif
}
@@ -493,7 +493,7 @@ static void set_viewport_source(struct vo *vo, struct mp_rect src)
if (p->force_window)
return;
- if (wl->video_viewport && !mp_rect_equals(&p->src, &src)) {
+ if (!mp_rect_equals(&p->src, &src)) {
wp_viewport_set_source(wl->video_viewport, src.x0 << 8,
src.y0 << 8, mp_rect_w(src) << 8,
mp_rect_h(src) << 8);
@@ -528,15 +528,18 @@ static void resize(struct vo *vo)
vo_get_src_dst_rects(vo, &src, &dst, &p->screen_osd_res);
int window_w = p->screen_osd_res.ml + p->screen_osd_res.mr + mp_rect_w(dst);
int window_h = p->screen_osd_res.mt + p->screen_osd_res.mb + mp_rect_h(dst);
- wp_viewport_set_destination(wl->viewport, window_w, window_h);
+ wp_viewport_set_destination(wl->viewport, lround(window_w / wl->scaling),
+ lround(window_h / wl->scaling));
//now we restore pan for video viewport calculation
vo->opts->pan_x = vo_opts->pan_x;
vo->opts->pan_y = vo_opts->pan_y;
vo_get_src_dst_rects(vo, &src, &dst, &p->screen_osd_res);
- wp_viewport_set_destination(wl->video_viewport, mp_rect_w(dst), mp_rect_h(dst));
+ wp_viewport_set_destination(wl->video_viewport, lround(mp_rect_w(dst) / wl->scaling),
+ lround(mp_rect_h(dst) / wl->scaling));
wl_subsurface_set_position(wl->video_subsurface, dst.x0, dst.y0);
- wp_viewport_set_destination(wl->osd_viewport, vo->dwidth, vo->dheight);
+ wp_viewport_set_destination(wl->osd_viewport, lround(vo->dwidth / wl->scaling),
+ lround(vo->dheight / wl->scaling));
wl_subsurface_set_position(wl->osd_subsurface, 0 - dst.x0, 0 - dst.y0);
set_viewport_source(vo, src);
}
@@ -692,10 +695,7 @@ done:
if (!vo_wayland_reconfig(vo))
return VO_ERROR;
- // mpv rotates clockwise but the wayland spec has counter-clockwise rotations
- // swap 1 and 3 to match mpv's direction
- int transform = (360 - img->params.rotate) % 360 / 90;
- wl_surface_set_buffer_transform(vo->wl->video_surface, transform);
+ wl_surface_set_buffer_transform(vo->wl->video_surface, img->params.rotate / 90);
// Immediately destroy all buffers if params change.
destroy_buffers(vo);
@@ -781,12 +781,6 @@ static int preinit(struct vo *vo)
goto err;
}
- if (!vo->wl->viewport) {
- MP_FATAL(vo->wl, "Compositor doesn't support the %s protocol!\n",
- wp_viewporter_interface.name);
- goto err;
- }
-
if (vo->wl->single_pixel_manager) {
#if HAVE_WAYLAND_PROTOCOLS_1_27
p->solid_buffer = wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(
diff --git a/video/out/vo_drm.c b/video/out/vo_drm.c
index aae73f7..34726a3 100644
--- a/video/out/vo_drm.c
+++ b/video/out/vo_drm.c
@@ -24,7 +24,6 @@
#include <unistd.h>
#include <drm_fourcc.h>
-#include <libswscale/swscale.h>
#include "common/msg.h"
#include "drm_atomic.h"
@@ -38,11 +37,12 @@
#include "vo.h"
#define IMGFMT_XRGB8888 IMGFMT_BGR0
-#if BYTE_ORDER == BIG_ENDIAN
-#define IMGFMT_XRGB2101010 pixfmt2imgfmt(AV_PIX_FMT_GBRP10BE)
-#else
-#define IMGFMT_XRGB2101010 pixfmt2imgfmt(AV_PIX_FMT_GBRP10LE)
-#endif
+#define IMGFMT_XBGR8888 IMGFMT_RGB0
+#define IMGFMT_XRGB2101010 \
+ pixfmt2imgfmt(MP_SELECT_LE_BE(AV_PIX_FMT_X2RGB10LE, AV_PIX_FMT_X2RGB10BE))
+#define IMGFMT_XBGR2101010 \
+ pixfmt2imgfmt(MP_SELECT_LE_BE(AV_PIX_FMT_X2BGR10LE, AV_PIX_FMT_X2BGR10BE))
+#define IMGFMT_YUYV pixfmt2imgfmt(AV_PIX_FMT_YUYV422)
#define BYTES_PER_PIXEL 4
#define BITS_PER_PIXEL 32
@@ -118,12 +118,31 @@ static struct framebuffer *setup_framebuffer(struct vo *vo)
fb->handle = creq.handle;
// select format
- if (drm->opts->drm_format == DRM_OPTS_FORMAT_XRGB2101010) {
+ switch (drm->opts->drm_format) {
+ case DRM_OPTS_FORMAT_XRGB2101010:
p->drm_format = DRM_FORMAT_XRGB2101010;
p->imgfmt = IMGFMT_XRGB2101010;
- } else {
- p->drm_format = DRM_FORMAT_XRGB8888;;
+ break;
+ case DRM_OPTS_FORMAT_XBGR2101010:
+ p->drm_format = DRM_FORMAT_XRGB2101010;
+ p->imgfmt = IMGFMT_XRGB2101010;
+ break;
+ case DRM_OPTS_FORMAT_XBGR8888:
+ p->drm_format = DRM_FORMAT_XBGR8888;
+ p->imgfmt = IMGFMT_XBGR8888;
+ break;
+ case DRM_OPTS_FORMAT_YUYV:
+ p->drm_format = DRM_FORMAT_YUYV;
+ p->imgfmt = IMGFMT_YUYV;
+ break;
+ default:
+ if (drm->opts->drm_format != DRM_OPTS_FORMAT_XRGB8888) {
+ MP_VERBOSE(vo, "Requested format not supported by VO, "
+ "falling back to xrgb8888\n");
+ }
+ p->drm_format = DRM_FORMAT_XRGB8888;
p->imgfmt = IMGFMT_XRGB8888;
+ break;
}
// create framebuffer object for the dumb-buffer
@@ -172,14 +191,15 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
vo->dheight = drm->fb->height;
vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);
- int w = p->dst.x1 - p->dst.x0;
- int h = p->dst.y1 - p->dst.y0;
+ struct mp_imgfmt_desc fmt = mp_imgfmt_get_desc(p->imgfmt);
+ p->dst.x0 = MP_ALIGN_DOWN(p->dst.x0, fmt.align_x);
+ p->dst.y0 = MP_ALIGN_DOWN(p->dst.y0, fmt.align_y);
p->sws->src = *params;
p->sws->dst = (struct mp_image_params) {
.imgfmt = p->imgfmt,
- .w = w,
- .h = h,
+ .w = mp_rect_w(p->dst),
+ .h = mp_rect_h(p->dst),
.p_w = 1,
.p_h = 1,
};
@@ -200,6 +220,9 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
if (mp_sws_reinit(p->sws) < 0)
return -1;
+ mp_mutex_lock(&vo->params_mutex);
+ vo->target_params = &p->sws->dst; // essentially constant, so this is okay
+ mp_mutex_unlock(&vo->params_mutex);
vo->want_redraw = true;
return 0;
}
@@ -239,35 +262,10 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, struct framebuffer *buf)
osd_draw_on_image(vo->osd, p->osd, 0, 0, p->cur_frame);
}
- if (p->drm_format == DRM_FORMAT_XRGB2101010) {
- // Pack GBRP10 image into XRGB2101010 for DRM
- const int w = p->cur_frame->w;
- const int h = p->cur_frame->h;
-
- const int g_padding = p->cur_frame->stride[0]/sizeof(uint16_t) - w;
- const int b_padding = p->cur_frame->stride[1]/sizeof(uint16_t) - w;
- const int r_padding = p->cur_frame->stride[2]/sizeof(uint16_t) - w;
- const int fbuf_padding = buf->stride/sizeof(uint32_t) - w;
-
- uint16_t *g_ptr = (uint16_t*)p->cur_frame->planes[0];
- uint16_t *b_ptr = (uint16_t*)p->cur_frame->planes[1];
- uint16_t *r_ptr = (uint16_t*)p->cur_frame->planes[2];
- uint32_t *fbuf_ptr = (uint32_t*)buf->map;
- for (unsigned y = 0; y < h; ++y) {
- for (unsigned x = 0; x < w; ++x) {
- *fbuf_ptr++ = (*r_ptr++ << 20) | (*g_ptr++ << 10) | (*b_ptr++);
- }
- g_ptr += g_padding;
- b_ptr += b_padding;
- r_ptr += r_padding;
- fbuf_ptr += fbuf_padding;
- }
- } else { // p->drm_format == DRM_FORMAT_XRGB8888
- memcpy_pic(buf->map, p->cur_frame->planes[0],
- p->cur_frame->w * BYTES_PER_PIXEL, p->cur_frame->h,
- buf->stride,
- p->cur_frame->stride[0]);
- }
+ memcpy_pic(buf->map, p->cur_frame->planes[0],
+ p->cur_frame->w * BYTES_PER_PIXEL, p->cur_frame->h,
+ buf->stride,
+ p->cur_frame->stride[0]);
}
if (mpi != p->last_input) {
@@ -423,7 +421,8 @@ err:
static int query_format(struct vo *vo, int format)
{
- return sws_isSupportedInput(imgfmt2pixfmt(format));
+ struct priv *p = vo->priv;
+ return mp_sws_supports_formats(p->sws, p->imgfmt, format) ? 1 : 0;
}
static int control(struct vo *vo, uint32_t request, void *arg)
diff --git a/video/out/vo_gpu.c b/video/out/vo_gpu.c
index c02e6e7..d49a6ba 100644
--- a/video/out/vo_gpu.c
+++ b/video/out/vo_gpu.c
@@ -80,11 +80,16 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
if (!sw->fns->start_frame(sw, &fbo))
return;
- gl_video_render_frame(p->renderer, frame, fbo, RENDER_FRAME_DEF);
+ gl_video_render_frame(p->renderer, frame, &fbo, RENDER_FRAME_DEF);
if (!sw->fns->submit_frame(sw, frame)) {
MP_ERR(vo, "Failed presenting frame!\n");
return;
}
+
+ struct mp_image_params *params = gl_video_get_target_params_ptr(p->renderer);
+ mp_mutex_lock(&vo->params_mutex);
+ vo->target_params = params;
+ mp_mutex_unlock(&vo->params_mutex);
}
static void flip_page(struct vo *vo)
@@ -169,13 +174,14 @@ static void get_and_update_ambient_lighting(struct gpu_priv *p)
}
}
-static void update_ra_ctx_options(struct vo *vo)
+static void update_ra_ctx_options(struct vo *vo, struct ra_ctx_opts *ctx_opts)
{
struct gpu_priv *p = vo->priv;
-
- /* Only the alpha option has any runtime toggle ability. */
struct gl_video_opts *gl_opts = mp_get_config_group(p->ctx, vo->global, &gl_video_conf);
- p->ctx->opts.want_alpha = gl_opts->alpha_mode == 1;
+ ctx_opts->want_alpha = (gl_opts->background == BACKGROUND_COLOR &&
+ gl_opts->background_color.a != 255) ||
+ gl_opts->background == BACKGROUND_NONE;
+ talloc_free(gl_opts);
}
static int control(struct vo *vo, uint32_t request, void *data)
@@ -186,9 +192,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_SET_PANSCAN:
resize(vo);
return VO_TRUE;
- case VOCTRL_SET_EQUALIZER:
- vo->want_redraw = true;
- return VO_TRUE;
case VOCTRL_SCREENSHOT: {
struct vo_frame *frame = vo_get_current_vo_frame(vo);
if (frame)
@@ -200,12 +203,14 @@ static int control(struct vo *vo, uint32_t request, void *data)
request_hwdec_api(vo, data);
return true;
case VOCTRL_UPDATE_RENDER_OPTS: {
- update_ra_ctx_options(vo);
+ struct ra_ctx_opts *ctx_opts = mp_get_config_group(vo, vo->global, &ra_ctx_conf);
+ update_ra_ctx_options(vo, ctx_opts);
gl_video_configure_queue(p->renderer, vo);
get_and_update_icc_profile(p);
if (p->ctx->fns->update_render_opts)
p->ctx->fns->update_render_opts(p->ctx);
vo->want_redraw = true;
+ talloc_free(ctx_opts);
return true;
}
case VOCTRL_RESET:
@@ -288,12 +293,9 @@ static int preinit(struct vo *vo)
p->log = vo->log;
struct ra_ctx_opts *ctx_opts = mp_get_config_group(vo, vo->global, &ra_ctx_conf);
- struct gl_video_opts *gl_opts = mp_get_config_group(vo, vo->global, &gl_video_conf);
- struct ra_ctx_opts opts = *ctx_opts;
- opts.want_alpha = gl_opts->alpha_mode == 1;
- p->ctx = ra_ctx_create(vo, opts);
+ update_ra_ctx_options(vo, ctx_opts);
+ p->ctx = ra_ctx_create(vo, *ctx_opts);
talloc_free(ctx_opts);
- talloc_free(gl_opts);
if (!p->ctx)
goto err_out;
assert(p->ctx->ra);
diff --git a/video/out/vo_gpu_next.c b/video/out/vo_gpu_next.c
index 1dc1b18..0a93f63 100644
--- a/video/out/vo_gpu_next.c
+++ b/video/out/vo_gpu_next.c
@@ -17,6 +17,9 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <dirent.h>
+#include <sys/stat.h>
+#include <time.h>
#include <unistd.h>
#include <libplacebo/colorspace.h>
@@ -29,7 +32,9 @@
#include "config.h"
#include "common/common.h"
+#include "misc/io_utils.h"
#include "options/m_config.h"
+#include "options/options.h"
#include "options/path.h"
#include "osdep/io.h"
#include "osdep/threads.h"
@@ -90,9 +95,12 @@ struct frame_info {
};
struct cache {
- char *path;
+ struct mp_log *log;
+ struct mpv_global *global;
+ char *dir;
+ const char *name;
+ size_t size_limit;
pl_cache cache;
- uint64_t sig;
};
struct priv {
@@ -131,21 +139,18 @@ struct priv {
pl_options pars;
struct m_config_cache *opts_cache;
+ struct m_config_cache *next_opts_cache;
+ struct gl_next_opts *next_opts;
struct cache shader_cache, icc_cache;
struct mp_csp_equalizer_state *video_eq;
struct scaler_params scalers[SCALER_COUNT];
const struct pl_hook **hooks; // storage for `params.hooks`
- enum mp_csp_levels output_levels;
- char **raw_opts;
+ enum pl_color_levels output_levels;
struct pl_icc_params icc_params;
char *icc_path;
pl_icc_object icc_profile;
- struct user_lut image_lut;
- struct user_lut target_lut;
- struct user_lut lut;
-
// Cached shaders, preserved across options updates
struct user_hook *user_hooks;
int num_user_hooks;
@@ -154,15 +159,59 @@ struct priv {
struct frame_info perf_fresh;
struct frame_info perf_redraw;
+ struct mp_image_params target_params;
+};
+
+static void update_render_options(struct vo *vo);
+static void update_lut(struct priv *p, struct user_lut *lut);
+
+struct gl_next_opts {
bool delayed_peak;
+ int border_background;
+ float corner_rounding;
bool inter_preserve;
+ struct user_lut lut;
+ struct user_lut image_lut;
+ struct user_lut target_lut;
bool target_hint;
+ char **raw_opts;
+};
- float corner_rounding;
+const struct m_opt_choice_alternatives lut_types[] = {
+ {"auto", PL_LUT_UNKNOWN},
+ {"native", PL_LUT_NATIVE},
+ {"normalized", PL_LUT_NORMALIZED},
+ {"conversion", PL_LUT_CONVERSION},
+ {0}
};
-static void update_render_options(struct vo *vo);
-static void update_lut(struct priv *p, struct user_lut *lut);
+#define OPT_BASE_STRUCT struct gl_next_opts
+const struct m_sub_options gl_next_conf = {
+ .opts = (const struct m_option[]) {
+ {"allow-delayed-peak-detect", OPT_BOOL(delayed_peak)},
+ {"border-background", OPT_CHOICE(border_background,
+ {"none", BACKGROUND_NONE},
+ {"color", BACKGROUND_COLOR},
+ {"tiles", BACKGROUND_TILES})},
+ {"corner-rounding", OPT_FLOAT(corner_rounding), M_RANGE(0, 1)},
+ {"interpolation-preserve", OPT_BOOL(inter_preserve)},
+ {"lut", OPT_STRING(lut.opt), .flags = M_OPT_FILE},
+ {"lut-type", OPT_CHOICE_C(lut.type, lut_types)},
+ {"image-lut", OPT_STRING(image_lut.opt), .flags = M_OPT_FILE},
+ {"image-lut-type", OPT_CHOICE_C(image_lut.type, lut_types)},
+ {"target-lut", OPT_STRING(target_lut.opt), .flags = M_OPT_FILE},
+ {"target-colorspace-hint", OPT_BOOL(target_hint)},
+ // No `target-lut-type` because we don't support non-RGB targets
+ {"libplacebo-opts", OPT_KEYVALUELIST(raw_opts)},
+ {0},
+ },
+ .defaults = &(struct gl_next_opts) {
+ .border_background = BACKGROUND_COLOR,
+ .inter_preserve = true,
+ },
+ .size = sizeof(struct gl_next_opts),
+ .change_flags = UPDATE_VIDEO,
+};
static pl_buf get_dr_buf(struct priv *p, const uint8_t *ptr)
{
@@ -238,8 +287,6 @@ static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h,
return mpi;
}
-static struct pl_color_space get_mpi_csp(struct vo *vo, struct mp_image *mpi);
-
static void update_overlays(struct vo *vo, struct mp_osd_res res,
int flags, enum pl_overlay_coords coords,
struct osd_state *state, struct pl_frame *frame,
@@ -290,6 +337,8 @@ static void update_overlays(struct vo *vo, struct mp_osd_res res,
entry->num_parts = 0;
for (int i = 0; i < item->num_parts; i++) {
const struct sub_bitmap *b = &item->parts[i];
+ if (b->dw == 0 || b->dh == 0)
+ continue;
uint32_t c = b->libass.color;
struct pl_overlay_part part = {
.src = { b->src_x, b->src_y, b->src_x + b->w, b->src_y + b->h },
@@ -322,7 +371,7 @@ static void update_overlays(struct vo *vo, struct mp_osd_res res,
ol->repr.alpha = PL_ALPHA_PREMULTIPLIED;
// Infer bitmap colorspace from source
if (src) {
- ol->color = get_mpi_csp(vo, src);
+ ol->color = src->params.color;
// Seems like HDR subtitles are targeting SDR white
if (pl_color_transfer_is_hdr(ol->color.transfer)) {
ol->color.hdr = (struct pl_hdr_metadata) {
@@ -332,6 +381,8 @@ static void update_overlays(struct vo *vo, struct mp_osd_res res,
}
break;
case SUBBITMAP_LIBASS:
+ if (src && item->video_color_space && !pl_color_space_is_hdr(&src->params.color))
+ ol->color = src->params.color;
ol->mode = PL_OVERLAY_MONOCHROME;
ol->repr.alpha = PL_ALPHA_INDEPENDENT;
break;
@@ -441,21 +492,15 @@ static int plane_data_from_imgfmt(struct pl_plane_data out_data[4],
return desc.num_planes;
}
-static struct pl_color_space get_mpi_csp(struct vo *vo, struct mp_image *mpi)
-{
- struct pl_color_space csp = {
- .primaries = mp_prim_to_pl(mpi->params.color.primaries),
- .transfer = mp_trc_to_pl(mpi->params.color.gamma),
- .hdr = mpi->params.color.hdr,
- };
- return csp;
-}
-
static bool hwdec_reconfig(struct priv *p, struct ra_hwdec *hwdec,
const struct mp_image_params *par)
{
if (p->hwdec_mapper) {
- if (mp_image_params_equal(par, &p->hwdec_mapper->src_params)) {
+ if (mp_image_params_static_equal(par, &p->hwdec_mapper->src_params)) {
+ p->hwdec_mapper->src_params.repr.dovi = par->repr.dovi;
+ p->hwdec_mapper->dst_params.repr.dovi = par->repr.dovi;
+ p->hwdec_mapper->src_params.color.hdr = par->color.hdr;
+ p->hwdec_mapper->dst_params.color.hdr = par->color.hdr;
return p->hwdec_mapper;
} else {
ra_hwdec_mapper_free(&p->hwdec_mapper);
@@ -571,12 +616,8 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
}
*frame = (struct pl_frame) {
- .color = get_mpi_csp(vo, mpi),
- .repr = {
- .sys = mp_csp_to_pl(par->color.space),
- .levels = mp_levels_to_pl(par->color.levels),
- .alpha = mp_alpha_to_pl(par->alpha),
- },
+ .color = par->color,
+ .repr = par->repr,
.profile = {
.data = mpi->icc_profile ? mpi->icc_profile->data : NULL,
.len = mpi->icc_profile ? mpi->icc_profile->size : 0,
@@ -588,14 +629,14 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
// mp_image, like AVFrame, likes communicating RGB/XYZ/YCbCr status
// implicitly via the image format, rather than the actual tagging.
switch (mp_imgfmt_get_forced_csp(par->imgfmt)) {
- case MP_CSP_RGB:
+ case PL_COLOR_SYSTEM_RGB:
frame->repr.sys = PL_COLOR_SYSTEM_RGB;
frame->repr.levels = PL_COLOR_LEVELS_FULL;
break;
- case MP_CSP_XYZ:
+ case PL_COLOR_SYSTEM_XYZ:
frame->repr.sys = PL_COLOR_SYSTEM_XYZ;
break;
- case MP_CSP_AUTO:
+ case PL_COLOR_SYSTEM_UNKNOWN:
if (!frame->repr.sys)
frame->repr.sys = pl_color_system_guess_ycbcr(par->w, par->h);
break;
@@ -664,10 +705,7 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
}
// Update chroma location, must be done after initializing planes
- pl_frame_set_chroma_location(frame, mp_chroma_to_pl(par->chroma_location));
-
- // Set the frame DOVI metadata
- mp_map_dovi_metadata_to_pl(mpi, frame);
+ pl_frame_set_chroma_location(frame, par->chroma_location);
if (mpi->film_grain)
pl_film_grain_from_av(&frame->film_grain, (AVFilmGrainParams *) mpi->film_grain->data);
@@ -679,9 +717,9 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
pl_icc_profile_compute_signature(&frame->profile);
// Update LUT attached to this frame
- update_lut(p, &p->image_lut);
- frame->lut = p->image_lut.lut;
- frame->lut_type = p->image_lut.type;
+ update_lut(p, &p->next_opts->image_lut);
+ frame->lut = p->next_opts->image_lut.lut;
+ frame->lut_type = p->next_opts->image_lut.type;
return true;
}
@@ -727,12 +765,14 @@ static void update_options(struct vo *vo)
{
struct priv *p = vo->priv;
pl_options pars = p->pars;
- if (m_config_cache_update(p->opts_cache))
+ bool changed = m_config_cache_update(p->opts_cache);
+ changed = m_config_cache_update(p->next_opts_cache) || changed;
+ if (changed)
update_render_options(vo);
- update_lut(p, &p->lut);
- pars->params.lut = p->lut.lut;
- pars->params.lut_type = p->lut.type;
+ update_lut(p, &p->next_opts->lut);
+ pars->params.lut = p->next_opts->lut.lut;
+ pars->params.lut_type = p->next_opts->lut.type;
// Update equalizer state
struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS;
@@ -744,7 +784,7 @@ static void update_options(struct vo *vo)
pars->color_adjustment.gamma = cparams.gamma;
p->output_levels = cparams.levels_out;
- for (char **kv = p->raw_opts; kv && kv[0]; kv += 2)
+ for (char **kv = p->next_opts->raw_opts; kv && kv[0]; kv += 2)
pl_options_set_str(pars, kv[0], kv[1]);
}
@@ -776,18 +816,18 @@ static void apply_target_contrast(struct priv *p, struct pl_color_space *color)
static void apply_target_options(struct priv *p, struct pl_frame *target)
{
- update_lut(p, &p->target_lut);
- target->lut = p->target_lut.lut;
- target->lut_type = p->target_lut.type;
+ update_lut(p, &p->next_opts->target_lut);
+ target->lut = p->next_opts->target_lut.lut;
+ target->lut_type = p->next_opts->target_lut.type;
// Colorspace overrides
const struct gl_video_opts *opts = p->opts_cache->opts;
if (p->output_levels)
- target->repr.levels = mp_levels_to_pl(p->output_levels);
+ target->repr.levels = p->output_levels;
if (opts->target_prim)
- target->color.primaries = mp_prim_to_pl(opts->target_prim);
+ target->color.primaries = opts->target_prim;
if (opts->target_trc)
- target->color.transfer = mp_trc_to_pl(opts->target_trc);
+ target->color.transfer = opts->target_trc;
// If swapchain returned a value use this, override is used in hint
if (opts->target_peak && !target->color.hdr.max_luma)
target->color.hdr.max_luma = opts->target_peak;
@@ -796,14 +836,23 @@ static void apply_target_options(struct priv *p, struct pl_frame *target)
if (opts->target_gamut) {
// Ensure resulting gamut still fits inside container
const struct pl_raw_primaries *gamut, *container;
- gamut = pl_raw_primaries_get(mp_prim_to_pl(opts->target_gamut));
+ gamut = pl_raw_primaries_get(opts->target_gamut);
container = pl_raw_primaries_get(target->color.primaries);
target->color.hdr.prim = pl_primaries_clip(gamut, container);
}
- if (opts->dither_depth > 0) {
+ int dither_depth = opts->dither_depth;
+ if (dither_depth == 0) {
+ struct ra_swapchain *sw = p->ra_ctx->swapchain;
+ if (sw->fns->color_depth) {
+ dither_depth = sw->fns->color_depth(sw);
+ } else if (!pl_color_transfer_is_hdr(target->color.transfer)) {
+ dither_depth = 8;
+ }
+ }
+ if (dither_depth > 0) {
struct pl_bit_encoding *tbits = &target->repr.bits;
- tbits->color_depth += opts->dither_depth - tbits->sample_depth;
- tbits->sample_depth = opts->dither_depth;
+ tbits->color_depth += dither_depth - tbits->sample_depth;
+ tbits->sample_depth = dither_depth;
}
if (opts->icc_opts->icc_use_luma) {
@@ -882,7 +931,7 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
params.info_callback = info_callback;
params.info_priv = vo;
params.skip_caching_single_frame = !cache_frame;
- params.preserve_mixing_cache = p->inter_preserve && !frame->still;
+ params.preserve_mixing_cache = p->next_opts->inter_preserve && !frame->still;
if (frame->still)
params.frame_mixer = NULL;
@@ -939,17 +988,17 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
p->last_id = id;
}
- if (p->target_hint && frame->current) {
- struct pl_color_space hint = get_mpi_csp(vo, frame->current);
+ if (p->next_opts->target_hint && frame->current) {
+ struct pl_color_space hint = frame->current->params.color;
if (opts->target_prim)
- hint.primaries = mp_prim_to_pl(opts->target_prim);
+ hint.primaries = opts->target_prim;
if (opts->target_trc)
- hint.transfer = mp_trc_to_pl(opts->target_trc);
+ hint.transfer = opts->target_trc;
if (opts->target_peak)
hint.hdr.max_luma = opts->target_peak;
apply_target_contrast(p, &hint);
pl_swapchain_colorspace_hint(p->sw, &hint);
- } else if (!p->target_hint) {
+ } else if (!p->next_opts->target_hint) {
pl_swapchain_colorspace_hint(p->sw, NULL);
}
@@ -959,14 +1008,15 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
if (!should_draw || !pl_swapchain_start_frame(p->sw, &swframe)) {
if (frame->current) {
// Advance the queue state to the current PTS to discard unused frames
- pl_queue_update(p->queue, NULL, pl_queue_params(
+ struct pl_queue_params qparams = *pl_queue_params(
.pts = frame->current->pts + pts_offset,
.radius = pl_frame_mix_radius(&params),
.vsync_duration = can_interpolate ? frame->ideal_frame_vsync_duration : 0,
+ );
#if PL_API_VER >= 340
- .drift_compensation = 0,
+ qparams.drift_compensation = 0;
#endif
- ));
+ pl_queue_update(p->queue, NULL, &qparams);
}
return;
}
@@ -992,10 +1042,10 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
.radius = pl_frame_mix_radius(&params),
.vsync_duration = can_interpolate ? frame->ideal_frame_vsync_duration : 0,
.interpolation_threshold = opts->interpolation_threshold,
+ );
#if PL_API_VER >= 340
- .drift_compensation = 0,
+ qparams.drift_compensation = 0;
#endif
- );
// Depending on the vsync ratio, we may be up to half of the vsync
// duration before the current frame time. This works fine because
@@ -1035,7 +1085,9 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
struct frame_priv *fp = mpi->priv;
apply_crop(image, p->src, vo->params->w, vo->params->h);
if (opts->blend_subs) {
- if (frame->redraw || fp->osd_sync < p->osd_sync) {
+ if (frame->redraw)
+ p->osd_sync++;
+ if (fp->osd_sync < p->osd_sync) {
float rx = pl_rect_w(p->dst) / pl_rect_w(image->crop);
float ry = pl_rect_h(p->dst) / pl_rect_h(image->crop);
struct mp_osd_res res = {
@@ -1047,9 +1099,6 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
.mb = (image->crop.y1 - vo->params->h) * ry,
.display_par = 1.0,
};
- // TODO: fix this doing pointless updates
- if (frame->redraw)
- p->osd_sync++;
update_overlays(vo, res, OSD_DRAW_SUB_ONLY,
PL_OVERLAY_COORDS_DST_CROP,
&fp->subs, image, mpi);
@@ -1078,12 +1127,27 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
goto done;
}
- const struct pl_frame *cur_frame = pl_frame_mix_nearest(&mix);
- if (cur_frame && vo->params) {
- vo->params->color.hdr = cur_frame->color.hdr;
+ struct pl_frame ref_frame;
+ pl_frames_infer_mix(p->rr, &mix, &target, &ref_frame);
+
+ mp_mutex_lock(&vo->params_mutex);
+ p->target_params = (struct mp_image_params){
+ .imgfmt_name = swframe.fbo->params.format
+ ? swframe.fbo->params.format->name : NULL,
+ .w = mp_rect_w(p->dst),
+ .h = mp_rect_h(p->dst),
+ .color = target.color,
+ .repr = target.repr,
+ .rotate = target.rotation,
+ };
+ vo->target_params = &p->target_params;
+
+ if (vo->params) {
+ vo->params->color.hdr = ref_frame.color.hdr;
// Augment metadata with peak detection max_pq_y / avg_pq_y
pl_renderer_get_hdr_metadata(p->rr, &vo->params->color.hdr);
}
+ mp_mutex_unlock(&vo->params_mutex);
p->is_interpolated = pts_offset != 0 && mix.num_frames > 1;
valid = true;
@@ -1168,6 +1232,9 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
return -1;
resize(vo);
+ mp_mutex_lock(&vo->params_mutex);
+ vo->target_params = NULL;
+ mp_mutex_unlock(&vo->params_mutex);
return 0;
}
@@ -1236,12 +1303,13 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args)
// Retrieve the current frame from the frame queue
struct pl_frame_mix mix;
enum pl_queue_status status;
- status = pl_queue_update(p->queue, &mix, pl_queue_params(
+ struct pl_queue_params qparams = *pl_queue_params(
.pts = p->last_pts,
+ );
#if PL_API_VER >= 340
- .drift_compensation = 0,
+ qparams.drift_compensation = 0;
#endif
- ));
+ status = pl_queue_update(p->queue, &mix, &qparams);
assert(status != PL_QUEUE_EOF);
if (status == PL_QUEUE_ERR) {
MP_ERR(vo, "Unknown error occurred while trying to take screenshot!\n");
@@ -1379,9 +1447,9 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args)
if (!args->res)
goto done;
- args->res->params.color.primaries = mp_prim_from_pl(target.color.primaries);
- args->res->params.color.gamma = mp_trc_from_pl(target.color.transfer);
- args->res->params.color.levels = mp_levels_from_pl(target.repr.levels);
+ args->res->params.color.primaries = target.color.primaries;
+ args->res->params.color.transfer = target.color.transfer;
+ args->res->params.repr.levels = target.repr.levels;
args->res->params.color.hdr = target.color.hdr;
if (args->scaled)
args->res->params.p_w = args->res->params.p_h = 1;
@@ -1424,6 +1492,19 @@ static inline void copy_frame_info_to_mp(struct frame_info *pl,
}
}
+static void update_ra_ctx_options(struct vo *vo, struct ra_ctx_opts *ctx_opts)
+{
+ struct priv *p = vo->priv;
+ struct gl_video_opts *gl_opts = p->opts_cache->opts;
+ bool border_alpha = (p->next_opts->border_background == BACKGROUND_COLOR &&
+ gl_opts->background_color.a != 255) ||
+ p->next_opts->border_background == BACKGROUND_NONE;
+ ctx_opts->want_alpha = (gl_opts->background == BACKGROUND_COLOR &&
+ gl_opts->background_color.a != 255) ||
+ gl_opts->background == BACKGROUND_NONE ||
+ border_alpha;
+}
+
static int control(struct vo *vo, uint32_t request, void *data)
{
struct priv *p = vo->priv;
@@ -1432,7 +1513,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_SET_PANSCAN:
resize(vo);
return VO_TRUE;
- case VOCTRL_SET_EQUALIZER:
case VOCTRL_PAUSE:
if (p->is_interpolated)
vo->want_redraw = true;
@@ -1440,8 +1520,7 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_UPDATE_RENDER_OPTS: {
m_config_cache_update(p->opts_cache);
- const struct gl_video_opts *opts = p->opts_cache->opts;
- p->ra_ctx->opts.want_alpha = opts->alpha_mode == ALPHA_YES;
+ update_ra_ctx_options(vo, &p->ra_ctx->opts);
if (p->ra_ctx->fns->update_render_opts)
p->ra_ctx->fns->update_render_opts(p->ra_ctx);
update_render_options(vo);
@@ -1513,84 +1592,190 @@ static void wait_events(struct vo *vo, int64_t until_time_ns)
}
}
-#if PL_API_VER < 342
-static inline void xor_hash(void *hash, pl_cache_obj obj)
+static char *cache_filepath(void *ta_ctx, char *dir, const char *prefix, uint64_t key)
{
- *((uint64_t *) hash) ^= obj.key;
+ bstr filename = {0};
+ bstr_xappend_asprintf(ta_ctx, &filename, "%s_%016" PRIx64, prefix, key);
+ return mp_path_join_bstr(ta_ctx, bstr0(dir), filename);
}
-static inline uint64_t pl_cache_signature(pl_cache cache)
+static pl_cache_obj cache_load_obj(void *p, uint64_t key)
{
- uint64_t hash = 0;
- pl_cache_iterate(cache, xor_hash, &hash);
- return hash;
+ struct cache *c = p;
+ void *ta_ctx = talloc_new(NULL);
+ pl_cache_obj obj = {0};
+
+ if (!c->dir)
+ goto done;
+
+ char *filepath = cache_filepath(ta_ctx, c->dir, c->name, key);
+ if (!filepath)
+ goto done;
+
+ if (stat(filepath, &(struct stat){0}))
+ goto done;
+
+ int64_t load_start = mp_time_ns();
+ struct bstr data = stream_read_file(filepath, ta_ctx, c->global, STREAM_MAX_READ_SIZE);
+ int64_t load_end = mp_time_ns();
+ MP_DBG(c, "%s: key(%" PRIx64 "), size(%zu), load time(%.3f ms)\n",
+ __func__, key, data.len,
+ MP_TIME_NS_TO_MS(load_end - load_start));
+
+ obj = (pl_cache_obj){
+ .key = key,
+ .data = talloc_steal(NULL, data.start),
+ .size = data.len,
+ .free = talloc_free,
+ };
+
+done:
+ talloc_free(ta_ctx);
+ return obj;
+}
+
+static void cache_save_obj(void *p, pl_cache_obj obj)
+{
+ if (!obj.data || !obj.size)
+ return;
+
+ const struct cache *c = p;
+ void *ta_ctx = talloc_new(NULL);
+
+ if (!c->dir)
+ goto done;
+
+ char *filepath = cache_filepath(ta_ctx, c->dir, c->name, obj.key);
+ if (!filepath)
+ goto done;
+
+ // Don't save if already exists
+ if (!stat(filepath, &(struct stat){0})) {
+ MP_DBG(c, "%s: key(%"PRIx64"), size(%zu)\n", __func__, obj.key, obj.size);
+ goto done;
+ }
+
+ int64_t save_start = mp_time_ns();
+ mp_save_to_file(filepath, obj.data, obj.size);
+ int64_t save_end = mp_time_ns();
+ MP_DBG(c, "%s: key(%" PRIx64 "), size(%zu), save time(%.3f ms)\n",
+ __func__, obj.key, obj.size,
+ MP_TIME_NS_TO_MS(save_end - save_start));
+
+done:
+ talloc_free(ta_ctx);
}
-#endif
static void cache_init(struct vo *vo, struct cache *cache, size_t max_size,
const char *dir_opt)
{
struct priv *p = vo->priv;
- const char *name = cache == &p->shader_cache ? "shader.cache" : "icc.cache";
+ const char *name = cache == &p->shader_cache ? "shader" : "icc";
+ const size_t limit = cache == &p->shader_cache ? 128 << 20 : 1536 << 20;
char *dir;
if (dir_opt && dir_opt[0]) {
- dir = mp_get_user_path(NULL, p->global, dir_opt);
+ dir = mp_get_user_path(vo, p->global, dir_opt);
} else {
- dir = mp_find_user_file(NULL, p->global, "cache", "");
+ dir = mp_find_user_file(vo, p->global, "cache", "");
}
if (!dir || !dir[0])
- goto done;
+ return;
mp_mkdirp(dir);
- cache->path = mp_path_join(vo, dir, name);
- cache->cache = pl_cache_create(pl_cache_params(
- .log = p->pllog,
- .max_total_size = max_size,
- ));
+ *cache = (struct cache){
+ .log = p->log,
+ .global = p->global,
+ .dir = dir,
+ .name = name,
+ .size_limit = limit,
+ .cache = pl_cache_create(pl_cache_params(
+ .log = p->pllog,
+ .get = cache_load_obj,
+ .set = cache_save_obj,
+ .priv = cache
+ )),
+ };
+}
- FILE *file = fopen(cache->path, "rb");
- if (file) {
- int ret = pl_cache_load_file(cache->cache, file);
- fclose(file);
- if (ret < 0)
- MP_WARN(p, "Failed loading cache from %s\n", cache->path);
- }
+struct file_entry {
+ char *filepath;
+ size_t size;
+ time_t atime;
+};
- cache->sig = pl_cache_signature(cache->cache);
-done:
- talloc_free(dir);
+static int compare_atime(const void *a, const void *b)
+{
+ return (((struct file_entry *)b)->atime - ((struct file_entry *)a)->atime);
}
static void cache_uninit(struct priv *p, struct cache *cache)
{
if (!cache->cache)
- goto done;
- if (pl_cache_signature(cache->cache) == cache->sig)
- goto done; // skip re-saving identical cache
+ return;
- assert(cache->path);
- char *tmp = talloc_asprintf(cache->path, "%sXXXXXX", cache->path);
- int fd = mkstemp(tmp);
- if (fd < 0)
- goto done;
- FILE *file = fdopen(fd, "wb");
- if (!file) {
- close(fd);
- unlink(tmp);
+ void *ta_ctx = talloc_new(NULL);
+ struct file_entry *files = NULL;
+ size_t num_files = 0;
+ assert(cache->dir);
+ assert(cache->name);
+
+ DIR *d = opendir(cache->dir);
+ if (!d)
goto done;
+
+ struct dirent *dir;
+ while ((dir = readdir(d)) != NULL) {
+ char *filepath = mp_path_join(ta_ctx, cache->dir, dir->d_name);
+ if (!filepath)
+ continue;
+ struct stat filestat;
+ if (stat(filepath, &filestat))
+ continue;
+ if (!S_ISREG(filestat.st_mode))
+ continue;
+ bstr fname = bstr0(dir->d_name);
+ if (!bstr_eatstart0(&fname, cache->name))
+ continue;
+ if (!bstr_eatstart0(&fname, "_"))
+ continue;
+ if (fname.len != 16) // %016x
+ continue;
+ MP_TARRAY_APPEND(ta_ctx, files, num_files,
+ (struct file_entry){
+ .filepath = filepath,
+ .size = filestat.st_size,
+ .atime = filestat.st_atime,
+ });
}
- int ret = pl_cache_save_file(cache->cache, file);
- fclose(file);
- if (ret >= 0)
- ret = rename(tmp, cache->path);
- if (ret < 0) {
- MP_WARN(p, "Failed saving cache to %s\n", cache->path);
- unlink(tmp);
+ closedir(d);
+
+ if (!num_files)
+ goto done;
+
+ qsort(files, num_files, sizeof(struct file_entry), compare_atime);
+
+ time_t t = time(NULL);
+ size_t cache_size = 0;
+ size_t cache_limit = cache->size_limit ? cache->size_limit : SIZE_MAX;
+ for (int i = 0; i < num_files; i++) {
+ // Remove files that exceed the size limit but are older than one day.
+ // This allows for temporary maintaining a larger cache size while
+ // adjusting the configuration. The cache will be cleared the next day
+ // for unused entries. We don't need to be overly aggressive with cache
+ // cleaning; in most cases, it will not grow much, and in others, it may
+ // actually be useful to cache more.
+ cache_size += files[i].size;
+ double rel_use = difftime(t, files[i].atime);
+ if (cache_size > cache_limit && rel_use > 60 * 60 * 24) {
+ MP_VERBOSE(p, "Removing %s | size: %9zu bytes | last used: %9d seconds ago\n",
+ files[i].filepath, files[i].size, (int)rel_use);
+ unlink(files[i].filepath);
+ }
}
- // fall through
done:
+ talloc_free(ta_ctx);
pl_cache_destroy(&cache->cache);
}
@@ -1618,6 +1803,10 @@ static void uninit(struct vo *vo)
cache_uninit(p, &p->shader_cache);
cache_uninit(p, &p->icc_cache);
+ pl_lut_free(&p->next_opts->image_lut.lut);
+ pl_lut_free(&p->next_opts->lut.lut);
+ pl_lut_free(&p->next_opts->target_lut.lut);
+
pl_icc_close(&p->icc_profile);
pl_renderer_destroy(&p->rr);
@@ -1644,12 +1833,17 @@ static int preinit(struct vo *vo)
{
struct priv *p = vo->priv;
p->opts_cache = m_config_cache_alloc(p, vo->global, &gl_video_conf);
+ p->next_opts_cache = m_config_cache_alloc(p, vo->global, &gl_next_conf);
+ p->next_opts = p->next_opts_cache->opts;
p->video_eq = mp_csp_equalizer_create(p, vo->global);
p->global = vo->global;
p->log = vo->log;
struct gl_video_opts *gl_opts = p->opts_cache->opts;
- p->context = gpu_ctx_create(vo, gl_opts);
+ struct ra_ctx_opts *ctx_opts = mp_get_config_group(vo, vo->global, &ra_ctx_conf);
+ update_ra_ctx_options(vo, ctx_opts);
+ p->context = gpu_ctx_create(vo, ctx_opts);
+ talloc_free(ctx_opts);
if (!p->context)
goto err_out;
// For the time being
@@ -1857,6 +2051,7 @@ static void update_lut(struct priv *p, struct user_lut *lut)
MP_VERBOSE(p, "Loading custom LUT '%s'\n", fname);
struct bstr lutdata = stream_read_file(fname, p, p->global, 100000000); // 100 MB
lut->lut = pl_lut_parse_cube(p->pllog, lutdata.start, lutdata.len);
+ talloc_free(fname);
talloc_free(lutdata.start);
}
@@ -1934,15 +2129,27 @@ static void update_render_options(struct vo *vo)
pl_options pars = p->pars;
const struct gl_video_opts *opts = p->opts_cache->opts;
pars->params.antiringing_strength = opts->scaler[0].antiring;
- pars->params.background_color[0] = opts->background.r / 255.0;
- pars->params.background_color[1] = opts->background.g / 255.0;
- pars->params.background_color[2] = opts->background.b / 255.0;
- pars->params.background_transparency = 1.0 - opts->background.a / 255.0;
+ pars->params.background_color[0] = opts->background_color.r / 255.0;
+ pars->params.background_color[1] = opts->background_color.g / 255.0;
+ pars->params.background_color[2] = opts->background_color.b / 255.0;
+ pars->params.background_transparency = 1 - opts->background_color.a / 255.0;
pars->params.skip_anti_aliasing = !opts->correct_downscaling;
pars->params.disable_linear_scaling = !opts->linear_downscaling && !opts->linear_upscaling;
pars->params.disable_fbos = opts->dumb_mode == 1;
- pars->params.blend_against_tiles = opts->alpha_mode == ALPHA_BLEND_TILES;
- pars->params.corner_rounding = p->corner_rounding;
+
+#if PL_API_VER >= 346
+ int map_background_types[3] = {
+ PL_CLEAR_SKIP, // BACKGROUND_NONE
+ PL_CLEAR_COLOR, // BACKGROUND_COLOR
+ PL_CLEAR_TILES, // BACKGROUND_TILES
+ };
+ pars->params.background = map_background_types[opts->background];
+ pars->params.border = map_background_types[p->next_opts->border_background];
+#else
+ pars->params.blend_against_tiles = opts->background == BACKGROUND_TILES;
+#endif
+
+ pars->params.corner_rounding = p->next_opts->corner_rounding;
pars->params.correct_subpixel_offsets = !opts->scaler_resizes_only;
// Map scaler options as best we can
@@ -1976,7 +2183,7 @@ static void update_render_options(struct vo *vo)
pars->peak_detect_params.scene_threshold_low = opts->tone_map.scene_threshold_low;
pars->peak_detect_params.scene_threshold_high = opts->tone_map.scene_threshold_high;
pars->peak_detect_params.percentile = opts->tone_map.peak_percentile;
- pars->peak_detect_params.allow_delayed = p->delayed_peak;
+ pars->peak_detect_params.allow_delayed = p->next_opts->delayed_peak;
const struct pl_tone_map_function * const tone_map_funs[] = {
[TONE_MAPPING_AUTO] = &pl_tone_map_auto,
@@ -2055,16 +2262,6 @@ static void update_render_options(struct vo *vo)
pars->params.hooks = p->hooks;
}
-#define OPT_BASE_STRUCT struct priv
-
-const struct m_opt_choice_alternatives lut_types[] = {
- {"auto", PL_LUT_UNKNOWN},
- {"native", PL_LUT_NATIVE},
- {"normalized", PL_LUT_NORMALIZED},
- {"conversion", PL_LUT_CONVERSION},
- {0}
-};
-
const struct vo_driver video_out_gpu_next = {
.description = "Video output based on libplacebo",
.name = "gpu-next",
@@ -2083,22 +2280,4 @@ const struct vo_driver video_out_gpu_next = {
.wakeup = wakeup,
.uninit = uninit,
.priv_size = sizeof(struct priv),
- .priv_defaults = &(const struct priv) {
- .inter_preserve = true,
- },
-
- .options = (const struct m_option[]) {
- {"allow-delayed-peak-detect", OPT_BOOL(delayed_peak)},
- {"corner-rounding", OPT_FLOAT(corner_rounding), M_RANGE(0, 1)},
- {"interpolation-preserve", OPT_BOOL(inter_preserve)},
- {"lut", OPT_STRING(lut.opt), .flags = M_OPT_FILE},
- {"lut-type", OPT_CHOICE_C(lut.type, lut_types)},
- {"image-lut", OPT_STRING(image_lut.opt), .flags = M_OPT_FILE},
- {"image-lut-type", OPT_CHOICE_C(image_lut.type, lut_types)},
- {"target-lut", OPT_STRING(target_lut.opt), .flags = M_OPT_FILE},
- {"target-colorspace-hint", OPT_BOOL(target_hint)},
- // No `target-lut-type` because we don't support non-RGB targets
- {"libplacebo-opts", OPT_KEYVALUELIST(raw_opts)},
- {0}
- },
};
diff --git a/video/out/vo_image.c b/video/out/vo_image.c
index cc48ab3..84e164f 100644
--- a/video/out/vo_image.c
+++ b/video/out/vo_image.c
@@ -118,7 +118,7 @@ static void flip_page(struct vo *vo)
filename = mp_path_join(t, p->opts->outdir, filename);
MP_INFO(vo, "Saving %s\n", filename);
- write_image(p->current, p->opts->opts, filename, vo->global, vo->log);
+ write_image(p->current, p->opts->opts, filename, vo->global, vo->log, true);
talloc_free(t);
}
diff --git a/video/out/vo_kitty.c b/video/out/vo_kitty.c
index 7d548c7..f8f0b07 100644
--- a/video/out/vo_kitty.c
+++ b/video/out/vo_kitty.c
@@ -340,8 +340,9 @@ static int preinit(struct vo *vo)
mp_sws_enable_cmdline_opts(p->sws, vo->global);
#if HAVE_POSIX
- struct sigaction sa;
- sa.sa_handler = handle_winch;
+ struct sigaction sa = {
+ .sa_handler = handle_winch,
+ };
sigaction(SIGWINCH, &sa, &saved_sigaction);
#endif
diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c
index 7170c1d..2842335 100644
--- a/video/out/vo_lavc.c
+++ b/video/out/vo_lavc.c
@@ -23,6 +23,8 @@
#include <stdio.h>
#include <stdlib.h>
+#include <libplacebo/utils/libav.h>
+
#include "common/common.h"
#include "options/options.h"
#include "video/fmt-conversion.h"
@@ -112,8 +114,8 @@ static int reconfig2(struct vo *vo, struct mp_image *img)
encoder->width = width;
encoder->height = height;
encoder->pix_fmt = pix_fmt;
- encoder->colorspace = mp_csp_to_avcol_spc(params->color.space);
- encoder->color_range = mp_csp_levels_to_avcol_range(params->color.levels);
+ encoder->colorspace = pl_system_to_av(params->repr.sys);
+ encoder->color_range = pl_levels_to_av(params->repr.levels);
AVRational tb;
diff --git a/video/out/vo_libmpv.c b/video/out/vo_libmpv.c
index 972588e..7974eed 100644
--- a/video/out/vo_libmpv.c
+++ b/video/out/vo_libmpv.c
@@ -27,6 +27,10 @@
#include "libmpv.h"
+#if HAVE_MACOS_COCOA_CB
+#include "osdep/mac/app_bridge.h"
+#endif
+
/*
* mpv_render_context is managed by the host application - the host application
* can access it any time, even if the VO is destroyed (or not created yet).
@@ -608,9 +612,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_PAUSE:
vo->want_redraw = true;
return VO_TRUE;
- case VOCTRL_SET_EQUALIZER:
- vo->want_redraw = true;
- return VO_TRUE;
case VOCTRL_SET_PANSCAN:
mp_mutex_lock(&ctx->lock);
ctx->need_resize = true;
@@ -708,6 +709,13 @@ static void uninit(struct vo *vo)
static int preinit(struct vo *vo)
{
+#if HAVE_MACOS_COCOA_CB
+ cocoa_init_cocoa_cb();
+#else
+ if (vo->probing)
+ return -1;
+#endif
+
struct vo_priv *p = vo->priv;
struct mpv_render_context *ctx =
diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c
deleted file mode 100644
index 55f1a68..0000000
--- a/video/out/vo_rpi.c
+++ /dev/null
@@ -1,938 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <stdbool.h>
-#include <assert.h>
-
-#include <bcm_host.h>
-#include <interface/mmal/mmal.h>
-#include <interface/mmal/util/mmal_util.h>
-#include <interface/mmal/util/mmal_default_components.h>
-#include <interface/mmal/vc/mmal_vc_api.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <libavutil/rational.h>
-
-#include "common/common.h"
-#include "common/msg.h"
-#include "opengl/common.h"
-#include "options/m_config.h"
-#include "osdep/timer.h"
-#include "vo.h"
-#include "win_state.h"
-#include "video/mp_image.h"
-#include "sub/osd.h"
-
-#include "opengl/ra_gl.h"
-#include "gpu/video.h"
-
-struct mp_egl_rpi {
- struct mp_log *log;
- struct GL *gl;
- struct ra *ra;
- EGLDisplay egl_display;
- EGLConfig egl_config;
- EGLContext egl_context;
- EGLSurface egl_surface;
- // yep, the API keeps a pointer to it
- EGL_DISPMANX_WINDOW_T egl_window;
-};
-
-struct priv {
- DISPMANX_DISPLAY_HANDLE_T display;
- DISPMANX_ELEMENT_HANDLE_T window;
- DISPMANX_ELEMENT_HANDLE_T osd_overlay;
- DISPMANX_UPDATE_HANDLE_T update;
- uint32_t w, h;
- uint32_t x, y;
- double display_fps;
-
- double osd_pts;
- struct mp_osd_res osd_res;
- struct m_config_cache *opts_cache;
-
- struct mp_egl_rpi egl;
- struct gl_video *gl_video;
- struct mpgl_osd *osd;
-
- MMAL_COMPONENT_T *renderer;
- bool renderer_enabled;
-
- bool display_synced, skip_osd;
- struct mp_image *next_image;
-
- // for RAM input
- MMAL_POOL_T *swpool;
-
- mp_mutex display_mutex;
- mp_cond display_cond;
- int64_t vsync_counter;
- bool reload_display;
-
- int background_layer;
- int video_layer;
- int osd_layer;
-
- int display_nr;
- int layer;
- bool background;
- bool enable_osd;
-};
-
-// Magic alignments (in pixels) expected by the MMAL internals.
-#define ALIGN_W 32
-#define ALIGN_H 16
-
-static void recreate_renderer(struct vo *vo);
-
-static void *get_proc_address(const GLubyte *name)
-{
- void *p = eglGetProcAddress(name);
- // EGL 1.4 (supported by the RPI firmware) does not necessarily return
- // function pointers for core functions.
- if (!p) {
- void *h = dlopen("/opt/vc/lib/libbrcmGLESv2.so", RTLD_LAZY);
- if (h) {
- p = dlsym(h, name);
- dlclose(h);
- }
- }
- return p;
-}
-
-static EGLConfig select_fb_config_egl(struct mp_egl_rpi *p)
-{
- EGLint attributes[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_DEPTH_SIZE, 0,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_NONE
- };
-
- EGLint config_count;
- EGLConfig config;
-
- eglChooseConfig(p->egl_display, attributes, &config, 1, &config_count);
-
- if (!config_count) {
- MP_FATAL(p, "Could find EGL configuration!\n");
- return NULL;
- }
-
- return config;
-}
-
-static void mp_egl_rpi_destroy(struct mp_egl_rpi *p)
-{
- if (p->egl_display) {
- eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT);
- }
- if (p->egl_surface)
- eglDestroySurface(p->egl_display, p->egl_surface);
- if (p->egl_context)
- eglDestroyContext(p->egl_display, p->egl_context);
- p->egl_context = EGL_NO_CONTEXT;
- eglReleaseThread();
- p->egl_display = EGL_NO_DISPLAY;
- talloc_free(p->gl);
- p->gl = NULL;
-}
-
-static int mp_egl_rpi_init(struct mp_egl_rpi *p, DISPMANX_ELEMENT_HANDLE_T window,
- int w, int h)
-{
- p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (!eglInitialize(p->egl_display, NULL, NULL)) {
- MP_FATAL(p, "EGL failed to initialize.\n");
- goto fail;
- }
-
- eglBindAPI(EGL_OPENGL_ES_API);
-
- EGLConfig config = select_fb_config_egl(p);
- if (!config)
- goto fail;
-
- p->egl_window = (EGL_DISPMANX_WINDOW_T){
- .element = window,
- .width = w,
- .height = h,
- };
- p->egl_surface = eglCreateWindowSurface(p->egl_display, config,
- &p->egl_window, NULL);
-
- if (p->egl_surface == EGL_NO_SURFACE) {
- MP_FATAL(p, "Could not create EGL surface!\n");
- goto fail;
- }
-
- EGLint context_attributes[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE
- };
- p->egl_context = eglCreateContext(p->egl_display, config,
- EGL_NO_CONTEXT, context_attributes);
-
- if (p->egl_context == EGL_NO_CONTEXT) {
- MP_FATAL(p, "Could not create EGL context!\n");
- goto fail;
- }
-
- eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
- p->egl_context);
-
- p->gl = talloc_zero(NULL, struct GL);
-
- const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS);
- mpgl_load_functions(p->gl, get_proc_address, exts, p->log);
-
- if (!p->gl->version && !p->gl->es)
- goto fail;
-
- p->ra = ra_create_gl(p->gl, p->log);
- if (!p->ra)
- goto fail;
-
- return 0;
-
-fail:
- mp_egl_rpi_destroy(p);
- return -1;
-}
-
-// Make mpi point to buffer, assuming MMAL_ENCODING_I420.
-// buffer can be NULL.
-// Return the required buffer space.
-static size_t layout_buffer(struct mp_image *mpi, MMAL_BUFFER_HEADER_T *buffer,
- struct mp_image_params *params)
-{
- assert(params->imgfmt == IMGFMT_420P);
- mp_image_set_params(mpi, params);
- int w = MP_ALIGN_UP(params->w, ALIGN_W);
- int h = MP_ALIGN_UP(params->h, ALIGN_H);
- uint8_t *cur = buffer ? buffer->data : NULL;
- size_t size = 0;
- for (int i = 0; i < 3; i++) {
- int div = i ? 2 : 1;
- mpi->planes[i] = cur;
- mpi->stride[i] = w / div;
- size_t plane_size = h / div * mpi->stride[i];
- if (cur)
- cur += plane_size;
- size += plane_size;
- }
- return size;
-}
-
-static void update_osd(struct vo *vo)
-{
- struct priv *p = vo->priv;
- if (!p->enable_osd)
- return;
-
- if (!gl_video_check_osd_change(p->gl_video, &p->osd_res, p->osd_pts)) {
- p->skip_osd = true;
- return;
- }
-
- MP_STATS(vo, "start rpi_osd");
-
- struct vo_frame frame = {0};
- struct ra_fbo target = {
- .tex = ra_create_wrapped_fb(p->egl.ra, 0, p->osd_res.w, p->osd_res.h),
- .flip = true,
- };
- gl_video_set_osd_pts(p->gl_video, p->osd_pts);
- gl_video_render_frame(p->gl_video, &frame, target, RENDER_FRAME_DEF);
- ra_tex_free(p->egl.ra, &target.tex);
-
- MP_STATS(vo, "stop rpi_osd");
-}
-
-static void resize(struct vo *vo)
-{
- struct priv *p = vo->priv;
- MMAL_PORT_T *input = p->renderer->input[0];
-
- struct mp_rect src, dst;
-
- vo_get_src_dst_rects(vo, &src, &dst, &p->osd_res);
-
- int rotate[] = {MMAL_DISPLAY_ROT0,
- MMAL_DISPLAY_ROT90,
- MMAL_DISPLAY_ROT180,
- MMAL_DISPLAY_ROT270};
-
-
- int src_w = src.x1 - src.x0, src_h = src.y1 - src.y0,
- dst_w = dst.x1 - dst.x0, dst_h = dst.y1 - dst.y0;
- int p_x, p_y;
- av_reduce(&p_x, &p_y, dst_w * src_h, src_w * dst_h, 16000);
- MMAL_DISPLAYREGION_T dr = {
- .hdr = { .id = MMAL_PARAMETER_DISPLAYREGION,
- .size = sizeof(MMAL_DISPLAYREGION_T), },
- .src_rect = { .x = src.x0, .y = src.y0, .width = src_w, .height = src_h },
- .dest_rect = { .x = dst.x0 + p->x, .y = dst.y0 + p->y,
- .width = dst_w, .height = dst_h },
- .layer = p->video_layer,
- .display_num = p->display_nr,
- .pixel_x = p_x,
- .pixel_y = p_y,
- .transform = rotate[vo->params ? vo->params->rotate / 90 : 0],
- .fullscreen = vo->opts->fullscreen,
- .set = MMAL_DISPLAY_SET_SRC_RECT | MMAL_DISPLAY_SET_DEST_RECT |
- MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM |
- MMAL_DISPLAY_SET_PIXEL | MMAL_DISPLAY_SET_TRANSFORM |
- MMAL_DISPLAY_SET_FULLSCREEN,
- };
-
- if (vo->params && (vo->params->rotate % 180) == 90) {
- MPSWAP(int, dr.src_rect.x, dr.src_rect.y);
- MPSWAP(int, dr.src_rect.width, dr.src_rect.height);
- }
-
- if (mmal_port_parameter_set(input, &dr.hdr))
- MP_WARN(vo, "could not set video rectangle\n");
-
- if (p->gl_video)
- gl_video_resize(p->gl_video, &src, &dst, &p->osd_res);
-}
-
-static void destroy_overlays(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- if (p->window)
- vc_dispmanx_element_remove(p->update, p->window);
- p->window = 0;
-
- gl_video_uninit(p->gl_video);
- p->gl_video = NULL;
- ra_free(&p->egl.ra);
- mp_egl_rpi_destroy(&p->egl);
-
- if (p->osd_overlay)
- vc_dispmanx_element_remove(p->update, p->osd_overlay);
- p->osd_overlay = 0;
-}
-
-static int update_display_size(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- uint32_t n_w = 0, n_h = 0;
- if (graphics_get_display_size(0, &n_w, &n_h) < 0) {
- MP_FATAL(vo, "Could not get display size.\n");
- return -1;
- }
-
- if (p->w == n_w && p->h == n_h)
- return 0;
-
- p->w = n_w;
- p->h = n_h;
-
- MP_VERBOSE(vo, "Display size: %dx%d\n", p->w, p->h);
-
- return 0;
-}
-
-static int create_overlays(struct vo *vo)
-{
- struct priv *p = vo->priv;
- destroy_overlays(vo);
-
- if (!p->display)
- return -1;
-
- if (vo->opts->fullscreen && p->background) {
- // Use the whole screen.
- VC_RECT_T dst = {.width = p->w, .height = p->h};
- VC_RECT_T src = {.width = 1 << 16, .height = 1 << 16};
- VC_DISPMANX_ALPHA_T alpha = {
- .flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
- .opacity = 0xFF,
- };
-
- p->window = vc_dispmanx_element_add(p->update, p->display,
- p->background_layer,
- &dst, 0, &src,
- DISPMANX_PROTECTION_NONE,
- &alpha, 0, 0);
- if (!p->window) {
- MP_FATAL(vo, "Could not add DISPMANX element.\n");
- return -1;
- }
- }
-
- if (p->enable_osd) {
- VC_RECT_T dst = {.x = p->x, .y = p->y,
- .width = p->osd_res.w, .height = p->osd_res.h};
- VC_RECT_T src = {.width = p->osd_res.w << 16, .height = p->osd_res.h << 16};
- VC_DISPMANX_ALPHA_T alpha = {
- .flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE,
- .opacity = 0xFF,
- };
- p->osd_overlay = vc_dispmanx_element_add(p->update, p->display,
- p->osd_layer,
- &dst, 0, &src,
- DISPMANX_PROTECTION_NONE,
- &alpha, 0, 0);
- if (!p->osd_overlay) {
- MP_FATAL(vo, "Could not add DISPMANX element.\n");
- return -1;
- }
-
- if (mp_egl_rpi_init(&p->egl, p->osd_overlay,
- p->osd_res.w, p->osd_res.h) < 0)
- {
- MP_FATAL(vo, "EGL/GLES initialization for OSD renderer failed.\n");
- return -1;
- }
- p->gl_video = gl_video_init(p->egl.ra, vo->log, vo->global);
- gl_video_set_clear_color(p->gl_video, (struct m_color){.a = 0});
- gl_video_set_osd_source(p->gl_video, vo->osd);
- }
-
- p->display_fps = 0;
- TV_GET_STATE_RESP_T tvstate;
- TV_DISPLAY_STATE_T tvstate_disp;
- if (!vc_tv_get_state(&tvstate) && !vc_tv_get_display_state(&tvstate_disp)) {
- if (tvstate_disp.state & (VC_HDMI_HDMI | VC_HDMI_DVI)) {
- p->display_fps = tvstate_disp.display.hdmi.frame_rate;
-
- HDMI_PROPERTY_PARAM_T param = {
- .property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE,
- };
- if (!vc_tv_hdmi_get_property(&param) &&
- param.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC)
- p->display_fps = p->display_fps / 1.001;
- } else {
- p->display_fps = tvstate_disp.display.sdtv.frame_rate;
- }
- }
-
- resize(vo);
-
- vo_event(vo, VO_EVENT_WIN_STATE);
-
- vc_dispmanx_update_submit_sync(p->update);
- p->update = vc_dispmanx_update_start(10);
-
- return 0;
-}
-
-static int set_geometry(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- if (vo->opts->fullscreen) {
- vo->dwidth = p->w;
- vo->dheight = p->h;
- p->x = p->y = 0;
- } else {
- struct vo_win_geometry geo;
- struct mp_rect screenrc = {0, 0, p->w, p->h};
-
- vo_calc_window_geometry(vo, &screenrc, &geo);
- vo_apply_window_geometry(vo, &geo);
-
- p->x = geo.win.x0;
- p->y = geo.win.y0;
- }
-
- resize(vo);
-
- if (create_overlays(vo) < 0)
- return -1;
-
- return 0;
-}
-
-static void wait_next_vsync(struct vo *vo)
-{
- struct priv *p = vo->priv;
- mp_mutex_lock(&p->display_mutex);
- int64_t end = mp_time_ns() + MP_TIME_MS_TO_NS(50);
- int64_t old = p->vsync_counter;
- while (old == p->vsync_counter && !p->reload_display) {
- if (mp_cond_timedwait_until(&p->display_cond, &p->display_mutex, end))
- break;
- }
- mp_mutex_unlock(&p->display_mutex);
-}
-
-static void flip_page(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- if (!p->renderer_enabled)
- return;
-
- struct mp_image *mpi = p->next_image;
- p->next_image = NULL;
-
- // For OSD
- if (!p->skip_osd && p->egl.gl)
- eglSwapBuffers(p->egl.egl_display, p->egl.egl_surface);
- p->skip_osd = false;
-
- if (mpi) {
- MMAL_PORT_T *input = p->renderer->input[0];
- MMAL_BUFFER_HEADER_T *ref = (void *)mpi->planes[3];
-
- // Assume this field is free for use by us.
- ref->user_data = mpi;
-
- if (mmal_port_send_buffer(input, ref)) {
- MP_ERR(vo, "could not queue picture!\n");
- talloc_free(mpi);
- }
- }
-
- if (p->display_synced)
- wait_next_vsync(vo);
-}
-
-static void free_mmal_buffer(void *arg)
-{
- MMAL_BUFFER_HEADER_T *buffer = arg;
- mmal_buffer_header_release(buffer);
-}
-
-static void draw_frame(struct vo *vo, struct vo_frame *frame)
-{
- struct priv *p = vo->priv;
-
- if (!p->renderer_enabled)
- return;
-
- mp_image_t *mpi = NULL;
- if (!frame->redraw && !frame->repeat)
- mpi = mp_image_new_ref(frame->current);
-
- talloc_free(p->next_image);
- p->next_image = NULL;
-
- if (mpi)
- p->osd_pts = mpi->pts;
-
- // Redraw only if the OSD has meaningfully changed, which we assume it
- // hasn't when a frame is merely repeated for display sync.
- p->skip_osd = !frame->redraw && frame->repeat;
-
- if (!p->skip_osd && p->egl.gl)
- update_osd(vo);
-
- p->display_synced = frame->display_synced;
-
- if (mpi && mpi->imgfmt != IMGFMT_MMAL) {
- MMAL_BUFFER_HEADER_T *buffer = mmal_queue_wait(p->swpool->queue);
- if (!buffer) {
- talloc_free(mpi);
- MP_ERR(vo, "Can't allocate buffer.\n");
- return;
- }
- mmal_buffer_header_reset(buffer);
-
- struct mp_image *new_ref = mp_image_new_custom_ref(NULL, buffer,
- free_mmal_buffer);
- if (!new_ref) {
- mmal_buffer_header_release(buffer);
- talloc_free(mpi);
- MP_ERR(vo, "Out of memory.\n");
- return;
- }
-
- mp_image_setfmt(new_ref, IMGFMT_MMAL);
- new_ref->planes[3] = (void *)buffer;
-
- struct mp_image dmpi = {0};
- buffer->length = layout_buffer(&dmpi, buffer, vo->params);
- mp_image_copy(&dmpi, mpi);
-
- talloc_free(mpi);
- mpi = new_ref;
- }
-
- p->next_image = mpi;
-}
-
-static int query_format(struct vo *vo, int format)
-{
- return format == IMGFMT_MMAL || format == IMGFMT_420P;
-}
-
-static MMAL_FOURCC_T map_csp(enum mp_csp csp)
-{
- switch (csp) {
- case MP_CSP_BT_601: return MMAL_COLOR_SPACE_ITUR_BT601;
- case MP_CSP_BT_709: return MMAL_COLOR_SPACE_ITUR_BT709;
- case MP_CSP_SMPTE_240M: return MMAL_COLOR_SPACE_SMPTE240M;
- default: return MMAL_COLOR_SPACE_UNKNOWN;
- }
-}
-
-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
-{
- mmal_buffer_header_release(buffer);
-}
-
-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
-{
- struct mp_image *mpi = buffer->user_data;
- talloc_free(mpi);
-}
-
-static void disable_renderer(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- if (p->renderer_enabled) {
- mmal_port_disable(p->renderer->control);
- mmal_port_disable(p->renderer->input[0]);
-
- mmal_port_flush(p->renderer->control);
- mmal_port_flush(p->renderer->input[0]);
-
- mmal_component_disable(p->renderer);
- }
- mmal_pool_destroy(p->swpool);
- p->swpool = NULL;
- p->renderer_enabled = false;
-}
-
-static int reconfig(struct vo *vo, struct mp_image_params *params)
-{
- struct priv *p = vo->priv;
- MMAL_PORT_T *input = p->renderer->input[0];
- bool opaque = params->imgfmt == IMGFMT_MMAL;
-
- if (!p->display)
- return -1;
-
- disable_renderer(vo);
-
- input->format->encoding = opaque ? MMAL_ENCODING_OPAQUE : MMAL_ENCODING_I420;
- input->format->es->video.width = MP_ALIGN_UP(params->w, ALIGN_W);
- input->format->es->video.height = MP_ALIGN_UP(params->h, ALIGN_H);
- input->format->es->video.crop = (MMAL_RECT_T){0, 0, params->w, params->h};
- input->format->es->video.par = (MMAL_RATIONAL_T){params->p_w, params->p_h};
- input->format->es->video.color_space = map_csp(params->color.space);
-
- if (mmal_port_format_commit(input))
- return -1;
-
- input->buffer_num = MPMAX(input->buffer_num_min,
- input->buffer_num_recommended) + 3;
- input->buffer_size = MPMAX(input->buffer_size_min,
- input->buffer_size_recommended);
-
- if (!opaque) {
- size_t size = layout_buffer(&(struct mp_image){0}, NULL, params);
- if (input->buffer_size != size) {
- MP_FATAL(vo, "We disagree with MMAL about buffer sizes.\n");
- return -1;
- }
-
- p->swpool = mmal_pool_create(input->buffer_num, input->buffer_size);
- if (!p->swpool) {
- MP_FATAL(vo, "Could not allocate buffer pool.\n");
- return -1;
- }
- }
-
- if (set_geometry(vo) < 0)
- return -1;
-
- p->renderer_enabled = true;
-
- if (mmal_port_enable(p->renderer->control, control_port_cb))
- return -1;
-
- if (mmal_port_enable(input, input_port_cb))
- return -1;
-
- if (mmal_component_enable(p->renderer)) {
- MP_FATAL(vo, "Failed to enable video renderer.\n");
- return -1;
- }
-
- resize(vo);
-
- return 0;
-}
-
-static struct mp_image *take_screenshot(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- if (!p->display)
- return NULL;
-
- struct mp_image *img = mp_image_alloc(IMGFMT_BGR0, p->w, p->h);
- if (!img)
- return NULL;
-
- DISPMANX_RESOURCE_HANDLE_T resource =
- vc_dispmanx_resource_create(VC_IMAGE_ARGB8888,
- img->w | ((img->w * 4) << 16), img->h,
- &(int32_t){0});
- if (!resource)
- goto fail;
-
- if (vc_dispmanx_snapshot(p->display, resource, 0))
- goto fail;
-
- VC_RECT_T rc = {.width = img->w, .height = img->h};
- if (vc_dispmanx_resource_read_data(resource, &rc, img->planes[0], img->stride[0]))
- goto fail;
-
- vc_dispmanx_resource_delete(resource);
- return img;
-
-fail:
- vc_dispmanx_resource_delete(resource);
- talloc_free(img);
- return NULL;
-}
-
-static void set_fullscreen(struct vo *vo) {
- struct priv *p = vo->priv;
-
- if (p->renderer_enabled)
- set_geometry(vo);
- vo->want_redraw = true;
-}
-
-static int control(struct vo *vo, uint32_t request, void *data)
-{
- struct priv *p = vo->priv;
-
- switch (request) {
- case VOCTRL_VO_OPTS_CHANGED: {
- void *opt;
- while (m_config_cache_get_next_changed(p->opts_cache, &opt)) {
- struct mp_vo_opts *opts = p->opts_cache->opts;
- if (&opts->fullscreen == opt)
- set_fullscreen(vo);
- }
- return VO_TRUE;
- }
- case VOCTRL_SET_PANSCAN:
- if (p->renderer_enabled)
- resize(vo);
- vo->want_redraw = true;
- return VO_TRUE;
- case VOCTRL_REDRAW_FRAME:
- update_osd(vo);
- return VO_TRUE;
- case VOCTRL_SCREENSHOT_WIN:
- *(struct mp_image **)data = take_screenshot(vo);
- return VO_TRUE;
- case VOCTRL_CHECK_EVENTS: {
- mp_mutex_lock(&p->display_mutex);
- bool reload_required = p->reload_display;
- p->reload_display = false;
- mp_mutex_unlock(&p->display_mutex);
- if (reload_required)
- recreate_renderer(vo);
- return VO_TRUE;
- }
- case VOCTRL_GET_DISPLAY_FPS:
- *(double *)data = p->display_fps;
- return VO_TRUE;
- case VOCTRL_GET_DISPLAY_RES:
- ((int *)data)[0] = p->w;
- ((int *)data)[1] = p->h;
- return VO_TRUE;
- }
-
- return VO_NOTIMPL;
-}
-
-static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1,
- uint32_t param2)
-{
- struct vo *vo = callback_data;
- struct priv *p = vo->priv;
- mp_mutex_lock(&p->display_mutex);
- p->reload_display = true;
- mp_cond_signal(&p->display_cond);
- mp_mutex_unlock(&p->display_mutex);
- vo_wakeup(vo);
-}
-
-static void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *arg)
-{
- struct vo *vo = arg;
- struct priv *p = vo->priv;
- mp_mutex_lock(&p->display_mutex);
- p->vsync_counter += 1;
- mp_cond_signal(&p->display_cond);
- mp_mutex_unlock(&p->display_mutex);
-}
-
-static void destroy_dispmanx(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- disable_renderer(vo);
- destroy_overlays(vo);
-
- if (p->update)
- vc_dispmanx_update_submit_sync(p->update);
- p->update = 0;
-
- if (p->display) {
- vc_dispmanx_vsync_callback(p->display, NULL, NULL);
- vc_dispmanx_display_close(p->display);
- }
- p->display = 0;
-}
-
-static int recreate_dispmanx(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- p->display = vc_dispmanx_display_open(p->display_nr);
- p->update = vc_dispmanx_update_start(0);
- if (!p->display || !p->update) {
- MP_FATAL(vo, "Could not get DISPMANX objects.\n");
- if (p->display)
- vc_dispmanx_display_close(p->display);
- p->display = 0;
- p->update = 0;
- return -1;
- }
-
- update_display_size(vo);
-
- vc_dispmanx_vsync_callback(p->display, vsync_callback, vo);
-
- return 0;
-}
-
-static void recreate_renderer(struct vo *vo)
-{
- MP_WARN(vo, "Recreating renderer after display change.\n");
-
- destroy_dispmanx(vo);
- recreate_dispmanx(vo);
-
- if (vo->params) {
- if (reconfig(vo, vo->params) < 0)
- MP_FATAL(vo, "Recreation failed.\n");
- }
-}
-
-static void uninit(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- vc_tv_unregister_callback_full(tv_callback, vo);
-
- talloc_free(p->next_image);
-
- destroy_dispmanx(vo);
-
- if (p->renderer)
- mmal_component_release(p->renderer);
-
- mmal_vc_deinit();
-
- mp_cond_destroy(&p->display_cond);
- mp_mutex_destroy(&p->display_mutex);
-}
-
-static int preinit(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- p->background_layer = p->layer;
- p->video_layer = p->layer + 1;
- p->osd_layer = p->layer + 2;
-
- p->egl.log = vo->log;
-
- bcm_host_init();
-
- if (mmal_vc_init()) {
- MP_FATAL(vo, "Could not initialize MMAL.\n");
- return -1;
- }
-
- mp_mutex_init(&p->display_mutex);
- mp_cond_init(&p->display_cond);
-
- p->opts_cache = m_config_cache_alloc(p, vo->global, &vo_sub_opts);
-
- if (recreate_dispmanx(vo) < 0)
- goto fail;
-
- if (update_display_size(vo) < 0)
- goto fail;
-
- if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &p->renderer))
- {
- MP_FATAL(vo, "Could not create MMAL renderer.\n");
- goto fail;
- }
-
- vc_tv_register_callback(tv_callback, vo);
-
- return 0;
-
-fail:
- uninit(vo);
- return -1;
-}
-
-#define OPT_BASE_STRUCT struct priv
-static const struct m_option options[] = {
- {"display", OPT_INT(display_nr)},
- {"layer", OPT_INT(layer), OPTDEF_INT(-10)},
- {"background", OPT_BOOL(background)},
- {"osd", OPT_BOOL(enable_osd), OPTDEF_INT(1)},
- {0},
-};
-
-const struct vo_driver video_out_rpi = {
- .description = "Raspberry Pi (MMAL)",
- .name = "rpi",
- .caps = VO_CAP_ROTATE90,
- .preinit = preinit,
- .query_format = query_format,
- .reconfig = reconfig,
- .control = control,
- .draw_frame = draw_frame,
- .flip_page = flip_page,
- .uninit = uninit,
- .priv_size = sizeof(struct priv),
- .options = options,
- .options_prefix = "rpi",
-};
diff --git a/video/out/vo_sdl.c b/video/out/vo_sdl.c
index 5f4c027..71791e2 100644
--- a/video/out/vo_sdl.c
+++ b/video/out/vo_sdl.c
@@ -547,6 +547,10 @@ static void wait_events(struct vo *vo, int64_t until_time_ns)
case SDL_WINDOWEVENT_LEAVE:
mp_input_put_key(vo->input_ctx, MP_KEY_MOUSE_LEAVE);
break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ vo_event(vo, VO_EVENT_FOCUS);
+ break;
}
break;
case SDL_QUIT:
@@ -959,6 +963,9 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_UPDATE_WINDOW_TITLE:
SDL_SetWindowTitle(vc->window, (char *)data);
return true;
+ case VOCTRL_GET_FOCUSED:
+ *(bool *)data = SDL_GetWindowFlags(vc->window) & SDL_WINDOW_INPUT_FOCUS;
+ return VO_TRUE;
}
return VO_NOTIMPL;
}
diff --git a/video/out/vo_tct.c b/video/out/vo_tct.c
index 8859095..0ca6ea6 100644
--- a/video/out/vo_tct.c
+++ b/video/out/vo_tct.c
@@ -39,17 +39,28 @@
#define ALGO_PLAIN 1
#define ALGO_HALF_BLOCKS 2
-#define TERM_ESC_CLEAR_COLORS "\033[0m"
-#define TERM_ESC_COLOR256_BG "\033[48;5"
-#define TERM_ESC_COLOR256_FG "\033[38;5"
-#define TERM_ESC_COLOR24BIT_BG "\033[48;2"
-#define TERM_ESC_COLOR24BIT_FG "\033[38;2"
-
#define DEFAULT_WIDTH 80
#define DEFAULT_HEIGHT 25
+static const bstr TERM_ESC_CLEAR_COLORS = bstr0_lit("\033[0m");
+static const bstr TERM_ESC_COLOR256_BG = bstr0_lit("\033[48;5");
+static const bstr TERM_ESC_COLOR256_FG = bstr0_lit("\033[38;5");
+static const bstr TERM_ESC_COLOR24BIT_BG = bstr0_lit("\033[48;2");
+static const bstr TERM_ESC_COLOR24BIT_FG = bstr0_lit("\033[38;2");
+
+static const bstr UNICODE_LOWER_HALF_BLOCK = bstr0_lit("\xe2\x96\x84");
+
+#define WRITE_STR(str) fwrite((str), strlen(str), 1, stdout)
+
+enum vo_tct_buffering {
+ VO_TCT_BUFFER_PIXEL,
+ VO_TCT_BUFFER_LINE,
+ VO_TCT_BUFFER_FRAME
+};
+
struct vo_tct_opts {
int algo;
+ int buffering;
int width; // 0 -> default
int height; // 0 -> default
bool term256; // 0 -> true color
@@ -57,7 +68,7 @@ struct vo_tct_opts {
struct lut_item {
char str[4];
- int width;
+ uint8_t width;
};
struct priv {
@@ -69,6 +80,7 @@ struct priv {
struct mp_rect src;
struct mp_rect dst;
struct mp_sws_context *sws;
+ bstr frame_buf;
struct lut_item lut[256];
};
@@ -101,69 +113,65 @@ static int rgb_to_x256(uint8_t r, uint8_t g, uint8_t b)
return color_err <= gray_err ? 16 + color_index() : 232 + gray_index;
}
-static void print_seq3(struct lut_item *lut, const char* prefix,
+static void print_seq3(bstr *frame, struct lut_item *lut, bstr prefix,
uint8_t r, uint8_t g, uint8_t b)
{
-// The fwrite implementation is about 25% faster than the printf code
-// (even if we use *.s with the lut values), however,
-// on windows we need to use printf in order to translate escape sequences and
-// UTF8 output for the console.
-#ifndef _WIN32
- fputs(prefix, stdout);
- fwrite(lut[r].str, lut[r].width, 1, stdout);
- fwrite(lut[g].str, lut[g].width, 1, stdout);
- fwrite(lut[b].str, lut[b].width, 1, stdout);
- fputc('m', stdout);
-#else
- printf("%s;%d;%d;%dm", prefix, (int)r, (int)g, (int)b);
-#endif
+ bstr_xappend(NULL, frame, prefix);
+ bstr_xappend(NULL, frame, (bstr){ lut[r].str, lut[r].width });
+ bstr_xappend(NULL, frame, (bstr){ lut[g].str, lut[g].width });
+ bstr_xappend(NULL, frame, (bstr){ lut[b].str, lut[b].width });
+ bstr_xappend(NULL, frame, (bstr)bstr0_lit("m"));
}
-static void print_seq1(struct lut_item *lut, const char* prefix, uint8_t c)
+static void print_seq1(bstr *frame, struct lut_item *lut, bstr prefix, uint8_t c)
{
-#ifndef _WIN32
- fputs(prefix, stdout);
- fwrite(lut[c].str, lut[c].width, 1, stdout);
- fputc('m', stdout);
-#else
- printf("%s;%dm", prefix, (int)c);
-#endif
+ bstr_xappend(NULL, frame, prefix);
+ bstr_xappend(NULL, frame, (bstr){ lut[c].str, lut[c].width });
+ bstr_xappend(NULL, frame, (bstr)bstr0_lit("m"));
}
+static void print_buffer(bstr *frame)
+{
+ fwrite(frame->start, frame->len, 1, stdout);
+ frame->len = 0;
+}
-static void write_plain(
+static void write_plain(bstr *frame,
const int dwidth, const int dheight,
const int swidth, const int sheight,
const unsigned char *source, const int source_stride,
- bool term256, struct lut_item *lut)
+ bool term256, struct lut_item *lut, enum vo_tct_buffering buffering)
{
assert(source);
const int tx = (dwidth - swidth) / 2;
const int ty = (dheight - sheight) / 2;
for (int y = 0; y < sheight; y++) {
const unsigned char *row = source + y * source_stride;
- printf(TERM_ESC_GOTO_YX, ty + y, tx);
+ bstr_xappend_asprintf(NULL, frame, TERM_ESC_GOTO_YX, ty + y, tx);
for (int x = 0; x < swidth; x++) {
unsigned char b = *row++;
unsigned char g = *row++;
unsigned char r = *row++;
if (term256) {
- print_seq1(lut, TERM_ESC_COLOR256_BG, rgb_to_x256(r, g, b));
+ print_seq1(frame, lut, TERM_ESC_COLOR256_BG, rgb_to_x256(r, g, b));
} else {
- print_seq3(lut, TERM_ESC_COLOR24BIT_BG, r, g, b);
+ print_seq3(frame, lut, TERM_ESC_COLOR24BIT_BG, r, g, b);
}
- printf(" ");
+ bstr_xappend(NULL, frame, (bstr)bstr0_lit(" "));
+ if (buffering <= VO_TCT_BUFFER_PIXEL)
+ print_buffer(frame);
}
- printf(TERM_ESC_CLEAR_COLORS);
+ bstr_xappend(NULL, frame, TERM_ESC_CLEAR_COLORS);
+ if (buffering <= VO_TCT_BUFFER_LINE)
+ print_buffer(frame);
}
- printf("\n");
}
-static void write_half_blocks(
+static void write_half_blocks(bstr *frame,
const int dwidth, const int dheight,
const int swidth, const int sheight,
unsigned char *source, int source_stride,
- bool term256, struct lut_item *lut)
+ bool term256, struct lut_item *lut, enum vo_tct_buffering buffering)
{
assert(source);
const int tx = (dwidth - swidth) / 2;
@@ -171,7 +179,7 @@ static void write_half_blocks(
for (int y = 0; y < sheight * 2; y += 2) {
const unsigned char *row_up = source + y * source_stride;
const unsigned char *row_down = source + (y + 1) * source_stride;
- printf(TERM_ESC_GOTO_YX, ty + y / 2, tx);
+ bstr_xappend_asprintf(NULL, frame, TERM_ESC_GOTO_YX, ty + y / 2, tx);
for (int x = 0; x < swidth; x++) {
unsigned char b_up = *row_up++;
unsigned char g_up = *row_up++;
@@ -180,17 +188,20 @@ static void write_half_blocks(
unsigned char g_down = *row_down++;
unsigned char r_down = *row_down++;
if (term256) {
- print_seq1(lut, TERM_ESC_COLOR256_BG, rgb_to_x256(r_up, g_up, b_up));
- print_seq1(lut, TERM_ESC_COLOR256_FG, rgb_to_x256(r_down, g_down, b_down));
+ print_seq1(frame, lut, TERM_ESC_COLOR256_BG, rgb_to_x256(r_up, g_up, b_up));
+ print_seq1(frame, lut, TERM_ESC_COLOR256_FG, rgb_to_x256(r_down, g_down, b_down));
} else {
- print_seq3(lut, TERM_ESC_COLOR24BIT_BG, r_up, g_up, b_up);
- print_seq3(lut, TERM_ESC_COLOR24BIT_FG, r_down, g_down, b_down);
+ print_seq3(frame, lut, TERM_ESC_COLOR24BIT_BG, r_up, g_up, b_up);
+ print_seq3(frame, lut, TERM_ESC_COLOR24BIT_FG, r_down, g_down, b_down);
}
- printf("\xe2\x96\x84"); // UTF8 bytes of U+2584 (lower half block)
+ bstr_xappend(NULL, frame, UNICODE_LOWER_HALF_BLOCK);
+ if (buffering <= VO_TCT_BUFFER_PIXEL)
+ print_buffer(frame);
}
- printf(TERM_ESC_CLEAR_COLORS);
+ bstr_xappend(NULL, frame, TERM_ESC_CLEAR_COLORS);
+ if (buffering <= VO_TCT_BUFFER_LINE)
+ print_buffer(frame);
}
- printf("\n");
}
static void get_win_size(struct vo *vo, int *out_width, int *out_height) {
@@ -236,7 +247,7 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
if (mp_sws_reinit(p->sws) < 0)
return -1;
- printf(TERM_ESC_CLEAR_SCREEN);
+ WRITE_STR(TERM_ESC_CLEAR_SCREEN);
vo->want_redraw = true;
return 0;
@@ -262,27 +273,36 @@ static void flip_page(struct vo *vo)
if (vo->dwidth != width || vo->dheight != height)
reconfig(vo, vo->params);
+ WRITE_STR(TERM_ESC_SYNC_UPDATE_BEGIN);
+
+ p->frame_buf.len = 0;
if (p->opts.algo == ALGO_PLAIN) {
- write_plain(
+ write_plain(&p->frame_buf,
vo->dwidth, vo->dheight, p->swidth, p->sheight,
p->frame->planes[0], p->frame->stride[0],
- p->opts.term256, p->lut);
+ p->opts.term256, p->lut, p->opts.buffering);
} else {
- write_half_blocks(
+ write_half_blocks(&p->frame_buf,
vo->dwidth, vo->dheight, p->swidth, p->sheight,
p->frame->planes[0], p->frame->stride[0],
- p->opts.term256, p->lut);
+ p->opts.term256, p->lut, p->opts.buffering);
}
+
+ bstr_xappend(NULL, &p->frame_buf, (bstr)bstr0_lit("\n"));
+ if (p->opts.buffering <= VO_TCT_BUFFER_FRAME)
+ print_buffer(&p->frame_buf);
+
+ WRITE_STR(TERM_ESC_SYNC_UPDATE_END);
fflush(stdout);
}
static void uninit(struct vo *vo)
{
- printf(TERM_ESC_RESTORE_CURSOR);
- printf(TERM_ESC_NORMAL_SCREEN);
+ WRITE_STR(TERM_ESC_RESTORE_CURSOR);
+ WRITE_STR(TERM_ESC_NORMAL_SCREEN);
struct priv *p = vo->priv;
- if (p->frame)
- talloc_free(p->frame);
+ talloc_free(p->frame);
+ talloc_free(p->frame_buf.start);
}
static int preinit(struct vo *vo)
@@ -296,14 +316,19 @@ static int preinit(struct vo *vo)
p->sws->log = vo->log;
mp_sws_enable_cmdline_opts(p->sws, vo->global);
- for (int i = 0; i < 256; ++i) {
- char buff[8];
- p->lut[i].width = snprintf(buff, sizeof(buff), ";%d", i);
- memcpy(p->lut[i].str, buff, 4); // some strings may not end on a null byte, but that's ok.
+ for (int i = 0; i < MP_ARRAY_SIZE(p->lut); ++i) {
+ char* out = p->lut[i].str;
+ *out++ = ';';
+ if (i >= 100)
+ *out++ = '0' + (i / 100);
+ if (i >= 10)
+ *out++ = '0' + ((i / 10) % 10);
+ *out++ = '0' + (i % 10);
+ p->lut[i].width = out - p->lut[i].str;
}
- printf(TERM_ESC_HIDE_CURSOR);
- printf(TERM_ESC_ALT_SCREEN);
+ WRITE_STR(TERM_ESC_HIDE_CURSOR);
+ WRITE_STR(TERM_ESC_ALT_SCREEN);
return 0;
}
@@ -333,6 +358,7 @@ const struct vo_driver video_out_tct = {
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.opts.algo = ALGO_HALF_BLOCKS,
+ .opts.buffering = VO_TCT_BUFFER_LINE,
},
.options = (const m_option_t[]) {
{"algo", OPT_CHOICE(opts.algo,
@@ -341,6 +367,10 @@ const struct vo_driver video_out_tct = {
{"width", OPT_INT(opts.width)},
{"height", OPT_INT(opts.height)},
{"256", OPT_BOOL(opts.term256)},
+ {"buffering", OPT_CHOICE(opts.buffering,
+ {"pixel", VO_TCT_BUFFER_PIXEL},
+ {"line", VO_TCT_BUFFER_LINE},
+ {"frame", VO_TCT_BUFFER_FRAME})},
{0}
},
.options_prefix = "vo-tct",
diff --git a/video/out/vo_vaapi.c b/video/out/vo_vaapi.c
index 12888fe..2a24130 100644
--- a/video/out/vo_vaapi.c
+++ b/video/out/vo_vaapi.c
@@ -519,7 +519,7 @@ static bool render_to_screen(struct priv *p, struct mp_image *mpi)
CHECK_VA_STATUS(p, "vaAssociateSubpicture()");
}
- int flags = va_get_colorspace_flag(p->image_params.color.space) |
+ int flags = va_get_colorspace_flag(p->image_params.repr.sys) |
p->scaling | VA_FRAME_PICTURE;
status = vaPutSurface(p->display,
surface,
diff --git a/video/out/vo_vdpau.c b/video/out/vo_vdpau.c
index d6b261f..b22dcba 100644
--- a/video/out/vo_vdpau.c
+++ b/video/out/vo_vdpau.c
@@ -80,7 +80,7 @@ struct vdpctx {
struct mp_image *current_image;
int64_t current_pts;
- int current_duration;
+ int64_t current_duration;
int output_surface_w, output_surface_h;
int rotation;
@@ -104,9 +104,9 @@ struct vdpctx {
int surface_num; // indexes output_surfaces
int query_surface_num;
VdpTime recent_vsync_time;
- float user_fps;
+ double user_fps;
bool composite_detect;
- int vsync_interval;
+ int64_t vsync_interval;
uint64_t last_queue_time;
uint64_t queue_time[MAX_OUTPUT_SURFACES];
uint64_t last_ideal_time;
@@ -721,12 +721,12 @@ static int update_presentation_queue_status(struct vo *vo)
break;
if (vc->vsync_interval > 1) {
uint64_t qtime = vc->queue_time[vc->query_surface_num];
- int diff = ((int64_t)vtime - (int64_t)qtime) / 1e6;
- MP_TRACE(vo, "Queue time difference: %d ms\n", diff);
+ double diff = MP_TIME_NS_TO_MS((int64_t)vtime - (int64_t)qtime);
+ MP_TRACE(vo, "Queue time difference: %.4f ms\n", diff);
if (vtime < qtime + vc->vsync_interval / 2)
- MP_VERBOSE(vo, "Frame shown too early (%d ms)\n", diff);
+ MP_VERBOSE(vo, "Frame shown too early (%.4f ms)\n", diff);
if (vtime > qtime + vc->vsync_interval)
- MP_VERBOSE(vo, "Frame shown late (%d ms)\n", diff);
+ MP_VERBOSE(vo, "Frame shown late (%.4f ms)\n", diff);
}
vc->query_surface_num = WRAP_ADD(vc->query_surface_num, 1,
vc->num_output_surfaces);
@@ -754,8 +754,8 @@ static void flip_page(struct vo *vo)
struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
- int64_t pts_us = vc->current_pts;
- int duration = vc->current_duration;
+ int64_t pts_ns = vc->current_pts;
+ int64_t duration = vc->current_duration;
vc->dropped_frame = true; // changed at end if false
@@ -770,11 +770,6 @@ static void flip_page(struct vo *vo)
}
vc->vsync_interval = MPMAX(vc->vsync_interval, 1);
- if (duration > INT_MAX / 1000)
- duration = -1;
- else
- duration *= 1000;
-
if (vc->vsync_interval == 1)
duration = -1; // Make sure drop logic is disabled
@@ -782,8 +777,8 @@ static void flip_page(struct vo *vo)
vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
CHECK_VDP_WARNING(vo, "Error when calling vdp_presentation_queue_get_time");
- int64_t rel_pts_ns = (pts_us * 1000) - mp_time_ns();
- if (!pts_us || rel_pts_ns < 0)
+ int64_t rel_pts_ns = pts_ns - mp_time_ns();
+ if (!pts_ns || rel_pts_ns < 0)
rel_pts_ns = 0;
uint64_t now = vdp_time;
@@ -1076,9 +1071,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_SET_PANSCAN:
checked_resize(vo);
return VO_TRUE;
- case VOCTRL_SET_EQUALIZER:
- vo->want_redraw = true;
- return true;
case VOCTRL_RESET:
forget_frames(vo, true);
return true;
@@ -1124,7 +1116,7 @@ const struct vo_driver video_out_vdpau = {
{"denoise", OPT_FLOAT(denoise), M_RANGE(0, 1)},
{"sharpen", OPT_FLOAT(sharpen), M_RANGE(-1, 1)},
{"hqscaling", OPT_INT(hqscaling), M_RANGE(0, 9)},
- {"fps", OPT_FLOAT(user_fps)},
+ {"fps", OPT_DOUBLE(user_fps)},
{"composite-detect", OPT_BOOL(composite_detect), OPTDEF_INT(1)},
{"queuetime-windowed", OPT_INT(flip_offset_window), OPTDEF_INT(50)},
{"queuetime-fs", OPT_INT(flip_offset_fs), OPTDEF_INT(50)},
diff --git a/video/out/vo_wlshm.c b/video/out/vo_wlshm.c
index 1e5e009..0b63426 100644
--- a/video/out/vo_wlshm.c
+++ b/video/out/vo_wlshm.c
@@ -21,8 +21,6 @@
#include <time.h>
#include <unistd.h>
-#include <libswscale/swscale.h>
-
#include "osdep/endian.h"
#include "present_sync.h"
#include "sub/osd.h"
@@ -32,6 +30,8 @@
#include "vo.h"
#include "wayland_common.h"
+#define IMGFMT_WL_RGB MP_SELECT_LE_BE(IMGFMT_BGR0, IMGFMT_0RGB)
+
struct buffer {
struct vo *vo;
size_t size;
@@ -164,7 +164,8 @@ err:
static int query_format(struct vo *vo, int format)
{
- return sws_isSupportedInput(imgfmt2pixfmt(format));
+ struct priv *p = vo->priv;
+ return mp_sws_supports_formats(p->sws, IMGFMT_WL_RGB, format) ? 1 : 0;
}
static int reconfig(struct vo *vo, struct mp_image_params *params)
@@ -195,21 +196,26 @@ static int resize(struct vo *vo)
vo->dwidth = width;
vo->dheight = height;
vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);
+
p->sws->dst = (struct mp_image_params) {
- .imgfmt = MP_SELECT_LE_BE(IMGFMT_BGR0, IMGFMT_0RGB),
+ .imgfmt = IMGFMT_WL_RGB,
.w = width,
.h = height,
.p_w = 1,
.p_h = 1,
};
mp_image_params_guess_csp(&p->sws->dst);
+ mp_mutex_lock(&vo->params_mutex);
+ vo->target_params = &p->sws->dst;
+ mp_mutex_unlock(&vo->params_mutex);
+
while (p->free_buffers) {
buf = p->free_buffers;
p->free_buffers = buf->next;
talloc_free(buf);
}
- vo_wayland_handle_fractional_scale(wl);
+ vo_wayland_handle_scale(wl);
return mp_sws_reinit(p->sws);
}
diff --git a/video/out/vo_x11.c b/video/out/vo_x11.c
index fa93157..b637b59 100644
--- a/video/out/vo_x11.c
+++ b/video/out/vo_x11.c
@@ -266,6 +266,10 @@ static bool resize(struct vo *vo)
if (mp_sws_reinit(p->sws) < 0)
return false;
+
+ mp_mutex_lock(&vo->params_mutex);
+ vo->target_params = &p->sws->dst;
+ mp_mutex_unlock(&vo->params_mutex);
}
vo->want_redraw = true;
diff --git a/video/out/vo_xv.c b/video/out/vo_xv.c
index 6c776c5..d2d5b73 100644
--- a/video/out/vo_xv.c
+++ b/video/out/vo_xv.c
@@ -92,6 +92,7 @@ struct xvctx {
int Shmem_Flag;
XShmSegmentInfo Shminfo[MAX_BUFFERS];
int Shm_Warned_Slow;
+ struct mp_image_params dst_params;
};
#define MP_FOURCC(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((unsigned)(d)<<24))
@@ -401,7 +402,7 @@ static void read_xv_csp(struct vo *vo)
ctx->cached_csp = 0;
int bt709_enabled;
if (xv_get_eq(vo, ctx->xv_port, "bt_709", &bt709_enabled))
- ctx->cached_csp = bt709_enabled == 100 ? MP_CSP_BT_709 : MP_CSP_BT_601;
+ ctx->cached_csp = bt709_enabled == 100 ? PL_COLOR_SYSTEM_BT_709 : PL_COLOR_SYSTEM_BT_601;
}
@@ -519,10 +520,17 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
ctx->current_buf = 0;
ctx->current_ip_buf = 0;
- int is_709 = params->color.space == MP_CSP_BT_709;
+ int is_709 = params->repr.sys == PL_COLOR_SYSTEM_BT_709;
xv_set_eq(vo, ctx->xv_port, "bt_709", is_709 * 200 - 100);
read_xv_csp(vo);
+ ctx->dst_params = *params;
+ if (ctx->cached_csp)
+ ctx->dst_params.repr.sys = ctx->cached_csp;
+ mp_mutex_lock(&vo->params_mutex);
+ vo->target_params = &ctx->dst_params;
+ mp_mutex_unlock(&vo->params_mutex);
+
resize(vo);
return 0;
@@ -652,7 +660,7 @@ static struct mp_image get_xv_buffer(struct vo *vo, int buf_index)
if (vo->params) {
struct mp_image_params params = *vo->params;
if (ctx->cached_csp)
- params.color.space = ctx->cached_csp;
+ params.repr.sys = ctx->cached_csp;
mp_image_set_attributes(&img, &params);
}
diff --git a/video/out/vulkan/common.h b/video/out/vulkan/common.h
index d006942..e75cb22 100644
--- a/video/out/vulkan/common.h
+++ b/video/out/vulkan/common.h
@@ -23,7 +23,6 @@
#define VK_USE_PLATFORM_WIN32_KHR
#endif
#if HAVE_COCOA
-#define VK_USE_PLATFORM_MACOS_MVK
#define VK_USE_PLATFORM_METAL_EXT
#endif
diff --git a/video/out/vulkan/context.c b/video/out/vulkan/context.c
index 5087403..82c878a 100644
--- a/video/out/vulkan/context.c
+++ b/video/out/vulkan/context.c
@@ -25,6 +25,7 @@
#include "options/m_config.h"
#include "video/out/placebo/ra_pl.h"
+#include "video/out/placebo/utils.h"
#include "context.h"
#include "utils.h"
@@ -37,39 +38,32 @@ struct vulkan_opts {
bool async_compute;
};
-static int vk_validate_dev(struct mp_log *log, const struct m_option *opt,
- struct bstr name, const char **value)
+static inline OPT_STRING_VALIDATE_FUNC(vk_validate_dev)
{
- struct bstr param = bstr0(*value);
int ret = M_OPT_INVALID;
- VkResult res;
+ void *ta_ctx = talloc_new(NULL);
+ pl_log pllog = mppl_log_create(ta_ctx, log);
+ if (!pllog)
+ goto done;
// Create a dummy instance to validate/list the devices
- VkInstanceCreateInfo info = {
- .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
- .pApplicationInfo = &(VkApplicationInfo) {
- .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
- .apiVersion = VK_API_VERSION_1_1,
- }
- };
-
- VkInstance inst;
- VkPhysicalDevice *devices = NULL;
- uint32_t num = 0;
-
- res = vkCreateInstance(&info, NULL, &inst);
- if (res != VK_SUCCESS)
+ mppl_log_set_probing(pllog, true);
+ pl_vk_inst inst = pl_vk_inst_create(pllog, pl_vk_inst_params());
+ mppl_log_set_probing(pllog, false);
+ if (!inst)
goto done;
- res = vkEnumeratePhysicalDevices(inst, &num, NULL);
+ uint32_t num = 0;
+ VkResult res = vkEnumeratePhysicalDevices(inst->instance, &num, NULL);
if (res != VK_SUCCESS)
goto done;
- devices = talloc_array(NULL, VkPhysicalDevice, num);
- res = vkEnumeratePhysicalDevices(inst, &num, devices);
+ VkPhysicalDevice *devices = talloc_array(ta_ctx, VkPhysicalDevice, num);
+ res = vkEnumeratePhysicalDevices(inst->instance, &num, devices);
if (res != VK_SUCCESS)
goto done;
+ struct bstr param = bstr0(*value);
bool help = bstr_equals0(param, "help");
if (help) {
mp_info(log, "Available vulkan devices:\n");
@@ -111,7 +105,9 @@ static int vk_validate_dev(struct mp_log *log, const struct m_option *opt,
BSTR_P(param));
done:
- talloc_free(devices);
+ pl_vk_inst_destroy(&inst);
+ pl_log_destroy(&pllog);
+ talloc_free(ta_ctx);
return ret;
}
@@ -207,8 +203,7 @@ bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk,
VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME,
VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME,
VK_KHR_VIDEO_QUEUE_EXTENSION_NAME,
- // This is a literal string as it's not in the official headers yet.
- "VK_MESA_video_decode_av1",
+ "VK_KHR_video_decode_av1", /* VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME */
};
VkPhysicalDeviceDescriptorBufferFeaturesEXT descriptor_buffer_feature = {
@@ -310,11 +305,6 @@ char *ra_vk_ctx_get_device_name(struct ra_ctx *ctx)
return device_name;
}
-static int color_depth(struct ra_swapchain *sw)
-{
- return 0; // TODO: implement this somehow?
-}
-
static bool start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo)
{
struct priv *p = sw->priv;
@@ -364,7 +354,6 @@ static void get_vsync(struct ra_swapchain *sw,
}
static const struct ra_swapchain_fns vulkan_swapchain = {
- .color_depth = color_depth,
.start_frame = start_frame,
.submit_frame = submit_frame,
.swap_buffers = swap_buffers,
diff --git a/video/out/vulkan/context_display.c b/video/out/vulkan/context_display.c
index 84cef1e..72f73ad 100644
--- a/video/out/vulkan/context_display.c
+++ b/video/out/vulkan/context_display.c
@@ -15,9 +15,11 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "common/common.h"
#include "context.h"
#include "options/m_config.h"
#include "utils.h"
+#include "video/out/placebo/utils.h"
#if HAVE_DRM
#include <errno.h>
@@ -214,35 +216,36 @@ done:
}
static int print_display_info(struct mp_log *log, const struct m_option *opt,
- struct bstr name) {
- VkResult res;
- VkPhysicalDevice *devices = NULL;
+ struct bstr name)
+{
+ void *ta_ctx = talloc_new(NULL);
+ pl_log pllog = mppl_log_create(ta_ctx, log);
+ if (!pllog)
+ goto done;
// Create a dummy instance to list the resources
- VkInstanceCreateInfo info = {
- .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
- .enabledExtensionCount = 1,
- .ppEnabledExtensionNames = (const char*[]) {
- VK_KHR_DISPLAY_EXTENSION_NAME
+ mppl_log_set_probing(pllog, true);
+ pl_vk_inst inst = pl_vk_inst_create(pllog, pl_vk_inst_params(
+ .extensions = (const char *[]){
+ VK_KHR_DISPLAY_EXTENSION_NAME,
},
- };
-
- VkInstance inst = NULL;
- res = vkCreateInstance(&info, NULL, &inst);
- if (res != VK_SUCCESS) {
+ .num_extensions = 1,
+ ));
+ mppl_log_set_probing(pllog, false);
+ if (!inst) {
mp_warn(log, "Unable to create Vulkan instance.\n");
goto done;
}
uint32_t num_devices = 0;
- vkEnumeratePhysicalDevices(inst, &num_devices, NULL);
- if (!num_devices) {
+ VkResult res = vkEnumeratePhysicalDevices(inst->instance, &num_devices, NULL);
+ if (res != VK_SUCCESS || !num_devices) {
mp_info(log, "No Vulkan devices detected.\n");
goto done;
}
- devices = talloc_array(NULL, VkPhysicalDevice, num_devices);
- vkEnumeratePhysicalDevices(inst, &num_devices, devices);
+ VkPhysicalDevice *devices = talloc_array(ta_ctx, VkPhysicalDevice, num_devices);
+ res = vkEnumeratePhysicalDevices(inst->instance, &num_devices, devices);
if (res != VK_SUCCESS) {
mp_warn(log, "Failed enumerating physical devices.\n");
goto done;
@@ -254,8 +257,9 @@ static int print_display_info(struct mp_log *log, const struct m_option *opt,
}
done:
- talloc_free(devices);
- vkDestroyInstance(inst, NULL);
+ pl_vk_inst_destroy(&inst);
+ pl_log_destroy(&pllog);
+ talloc_free(ta_ctx);
return M_OPT_EXIT;
}
@@ -296,7 +300,7 @@ static void open_render_fd(struct ra_ctx *ctx, const char *render_path)
p->drm_params.render_fd = open(render_path, O_RDWR | O_CLOEXEC);
if (p->drm_params.render_fd == -1) {
MP_WARN(ctx, "Failed to open render node: %s\n",
- strerror(errno));
+ mp_strerror(errno));
}
}
diff --git a/video/out/vulkan/context_mac.m b/video/out/vulkan/context_mac.m
index 8ac6e16..bedd0d4 100644
--- a/video/out/vulkan/context_mac.m
+++ b/video/out/vulkan/context_mac.m
@@ -15,8 +15,10 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#import <QuartzCore/QuartzCore.h>
+
#include "video/out/gpu/context.h"
-#include "osdep/macOS_swift.h"
+#include "osdep/mac/swift.h"
#include "common.h"
#include "context.h"
@@ -42,6 +44,12 @@ static void mac_vk_swap_buffers(struct ra_ctx *ctx)
[p->vo_mac swapBuffer];
}
+static void mac_vk_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info)
+{
+ struct priv *p = ctx->priv;
+ [p->vo_mac fillVsyncWithInfo:info];
+}
+
static bool mac_vk_init(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
@@ -56,7 +64,7 @@ static bool mac_vk_init(struct ra_ctx *ctx)
goto error;
VkMetalSurfaceCreateInfoEXT mac_info = {
- .sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK,
+ .sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT,
.pNext = NULL,
.flags = 0,
.pLayer = p->vo_mac.layer,
@@ -64,6 +72,7 @@ static bool mac_vk_init(struct ra_ctx *ctx)
struct ra_vk_ctx_params params = {
.swap_buffers = mac_vk_swap_buffers,
+ .get_vsync = mac_vk_get_vsync,
};
VkInstance inst = vk->vkinst->instance;
@@ -85,7 +94,14 @@ error:
static bool resize(struct ra_ctx *ctx)
{
- return ra_vk_ctx_resize(ctx, ctx->vo->dwidth, ctx->vo->dheight);
+ struct priv *p = ctx->priv;
+
+ if (!p->vo_mac.window) {
+ return false;
+ }
+ CGSize size = p->vo_mac.window.framePixel.size;
+
+ return ra_vk_ctx_resize(ctx, (int)size.width, (int)size.height);
}
static bool mac_vk_reconfig(struct ra_ctx *ctx)
diff --git a/video/out/vulkan/context_wayland.c b/video/out/vulkan/context_wayland.c
index 761ff5b..cdf1ba6 100644
--- a/video/out/vulkan/context_wayland.c
+++ b/video/out/vulkan/context_wayland.c
@@ -118,7 +118,7 @@ static bool resize(struct ra_ctx *ctx)
const int32_t height = mp_rect_h(wl->geometry);
vo_wayland_set_opaque_region(wl, ctx->opts.want_alpha);
- vo_wayland_handle_fractional_scale(wl);
+ vo_wayland_handle_scale(wl);
return ra_vk_ctx_resize(ctx, width, height);
}
diff --git a/video/out/vulkan/context_win.c b/video/out/vulkan/context_win.c
index a89c644..328753f 100644
--- a/video/out/vulkan/context_win.c
+++ b/video/out/vulkan/context_win.c
@@ -50,6 +50,9 @@ static bool win_init(struct ra_ctx *ctx)
if (!vo_w32_init(ctx->vo))
goto error;
+ if (ctx->opts.want_alpha)
+ vo_w32_set_transparency(ctx->vo, ctx->opts.want_alpha);
+
VkWin32SurfaceCreateInfoKHR wininfo = {
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
.hinstance = HINST_THISCOMPONENT,
@@ -96,11 +99,17 @@ static int win_control(struct ra_ctx *ctx, int *events, int request, void *arg)
return ret;
}
+static void win_update_render_opts(struct ra_ctx *ctx)
+{
+ vo_w32_set_transparency(ctx->vo, ctx->opts.want_alpha);
+}
+
const struct ra_ctx_fns ra_ctx_vulkan_win = {
- .type = "vulkan",
- .name = "winvk",
- .reconfig = win_reconfig,
- .control = win_control,
- .init = win_init,
- .uninit = win_uninit,
+ .type = "vulkan",
+ .name = "winvk",
+ .reconfig = win_reconfig,
+ .control = win_control,
+ .update_render_opts = win_update_render_opts,
+ .init = win_init,
+ .uninit = win_uninit,
};
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index e6a4670..36f48b9 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -40,6 +40,7 @@
#include "w32_common.h"
#include "win32/displayconfig.h"
#include "win32/droptarget.h"
+#include "win32/menu.h"
#include "osdep/io.h"
#include "osdep/threads.h"
#include "osdep/w32_keyboard.h"
@@ -58,10 +59,17 @@ EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif
+#ifndef DWMWA_VISIBLE_FRAME_BORDER_THICKNESS
+#define DWMWA_VISIBLE_FRAME_BORDER_THICKNESS 37
+#endif
-//Older MinGW compatibility
+#ifndef DWMWA_WINDOW_CORNER_PREFERENCE
#define DWMWA_WINDOW_CORNER_PREFERENCE 33
+#endif
+
+#ifndef DWMWA_SYSTEMBACKDROP_TYPE
#define DWMWA_SYSTEMBACKDROP_TYPE 38
+#endif
#ifndef DPI_ENUMS_DECLARED
typedef enum MONITOR_DPI_TYPE {
@@ -75,10 +83,12 @@ typedef enum MONITOR_DPI_TYPE {
#define rect_w(r) ((r).right - (r).left)
#define rect_h(r) ((r).bottom - (r).top)
+#define WM_SHOWMENU (WM_USER + 1)
+
struct w32_api {
HRESULT (WINAPI *pGetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
- BOOL (WINAPI *pImmDisableIME)(DWORD);
BOOL (WINAPI *pAdjustWindowRectExForDpi)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi);
+ int (WINAPI *pGetSystemMetricsForDpi)(int nIndex, UINT dpi);
BOOLEAN (WINAPI *pShouldAppsUseDarkMode)(void);
DWORD (WINAPI *pSetPreferredAppMode)(DWORD mode);
};
@@ -102,6 +112,8 @@ struct vo_w32_state {
HHOOK parent_win_hook;
HWINEVENTHOOK parent_evt_hook;
+ struct menu_ctx *menu_ctx;
+
HMONITOR monitor; // Handle of the current screen
char *color_profile; // Path of the current screen's color profile
@@ -132,7 +144,7 @@ struct vo_w32_state {
atomic_uint event_flags;
BOOL tracking;
- TRACKMOUSEEVENT trackEvent;
+ TRACKMOUSEEVENT track_event;
int mouse_x;
int mouse_y;
@@ -156,8 +168,8 @@ struct vo_w32_state {
ITaskbarList2 *taskbar_list;
ITaskbarList3 *taskbar_list3;
- UINT tbtnCreatedMsg;
- bool tbtnCreated;
+ UINT tbtn_created_msg;
+ bool tbtn_created;
struct voctrl_playback_state current_pstate;
@@ -181,8 +193,21 @@ struct vo_w32_state {
HANDLE avrt_handle;
bool cleared;
+ bool dragging;
+ bool start_dragging;
+ BOOL win_arranging;
+
+ bool conversion_mode_init;
+ bool unmaximize;
};
+static inline int get_system_metrics(struct vo_w32_state *w32, int metric)
+{
+ return w32->api.pGetSystemMetricsForDpi
+ ? w32->api.pGetSystemMetricsForDpi(metric, w32->dpi)
+ : GetSystemMetrics(metric);
+}
+
static void adjust_window_rect(struct vo_w32_state *w32, HWND hwnd, RECT *rc)
{
if (!w32->opts->border)
@@ -197,13 +222,67 @@ static void adjust_window_rect(struct vo_w32_state *w32, HWND hwnd, RECT *rc)
}
}
+static bool check_windows10_build(DWORD build)
+{
+ OSVERSIONINFOEXW osvi = {
+ .dwOSVersionInfoSize = sizeof(osvi),
+ .dwMajorVersion = HIBYTE(_WIN32_WINNT_WIN10),
+ .dwMinorVersion = LOBYTE(_WIN32_WINNT_WIN10),
+ .dwBuildNumber = build,
+ };
+
+ DWORD type = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER;
+
+ ULONGLONG mask = 0;
+ mask = VerSetConditionMask(mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+ mask = VerSetConditionMask(mask, VER_MINORVERSION, VER_GREATER_EQUAL);
+ mask = VerSetConditionMask(mask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
+
+ return VerifyVersionInfoW(&osvi, type, mask);
+}
+
+// Get adjusted title bar height, only relevant for --title-bar=no
+static int get_title_bar_height(struct vo_w32_state *w32)
+{
+ assert(!w32->opts->title_bar && w32->opts->border);
+ UINT visible_border = 0;
+ // Only available on Windows 11, check in case it's backported and breaks
+ // WM_NCCALCSIZE exception for Windows 10.
+ if (check_windows10_build(22000)) {
+ DwmGetWindowAttribute(w32->window, DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
+ &visible_border, sizeof(visible_border));
+ }
+ int top_bar = IsMaximized(w32->window)
+ ? get_system_metrics(w32, SM_CYFRAME) +
+ get_system_metrics(w32, SM_CXPADDEDBORDER)
+ : visible_border;
+ return top_bar;
+}
+
static void add_window_borders(struct vo_w32_state *w32, HWND hwnd, RECT *rc)
{
RECT win = *rc;
adjust_window_rect(w32, hwnd, rc);
// Adjust for title bar height that will be hidden in WM_NCCALCSIZE
- if (w32->opts->border && !w32->opts->title_bar && !w32->current_fs)
- rc->top -= rc->top - win.top;
+ // Keep the frame border. On Windows 10 the top border is not retained.
+ // It appears that DWM draws the title bar with its full height, extending
+ // outside the window area. Essentially, there is a bug in DWM, preventing
+ // the adjustment of the title bar height. This issue occurs when both the
+ // top and left client areas are non-zero in WM_NCCALCSIZE. If the left NC
+ // area is set to 0, the title bar is drawn correctly with the adjusted
+ // height. To mitigate this problem, set the top NC area to zero. The issue
+ // doesn't happen on Windows 11 or when DWM NC drawing is disabled with
+ // DWMWA_NCRENDERING_POLICY. We aim to avoid the manual drawing the border
+ // and want the DWM look and feel, so skip the top border on Windows 10.
+ // Also DWMWA_VISIBLE_FRAME_BORDER_THICKNESS is available only on Windows 11,
+ // so it would be hard to guess this size correctly on Windows 10 anyway.
+ if (w32->opts->border && !w32->opts->title_bar && !w32->current_fs &&
+ (GetWindowLongPtrW(w32->window, GWL_STYLE) & WS_CAPTION))
+ {
+ if (!check_windows10_build(22000) && !IsMaximized(w32->window))
+ *rc = win;
+ rc->top = win.top - get_title_bar_height(w32);
+ }
}
// basically a reverse AdjustWindowRect (win32 doesn't appear to have this)
@@ -226,13 +305,13 @@ static LRESULT borderless_nchittest(struct vo_w32_state *w32, int x, int y)
if (!GetWindowRect(w32->window, &rc))
return HTNOWHERE;
- POINT frame = {GetSystemMetrics(SM_CXSIZEFRAME),
- GetSystemMetrics(SM_CYSIZEFRAME)};
+ POINT frame = {get_system_metrics(w32, SM_CXSIZEFRAME),
+ get_system_metrics(w32, SM_CYSIZEFRAME)};
if (w32->opts->border) {
- frame.x += GetSystemMetrics(SM_CXPADDEDBORDER);
- frame.y += GetSystemMetrics(SM_CXPADDEDBORDER);
+ frame.x += get_system_metrics(w32, SM_CXPADDEDBORDER);
+ frame.y += get_system_metrics(w32, SM_CXPADDEDBORDER);
if (!w32->opts->title_bar)
- rc.top -= GetSystemMetrics(SM_CXPADDEDBORDER);
+ rc.top -= get_system_metrics(w32, SM_CXPADDEDBORDER);
}
InflateRect(&rc, -frame.x, -frame.y);
@@ -344,8 +423,8 @@ static void clear_keyboard_buffer(void)
// Use the method suggested by Michael Kaplan to clear any pending dead
// keys from the current keyboard layout. See:
- // https://web.archive.org/web/20101004154432/http://blogs.msdn.com/b/michkap/archive/2006/04/06/569632.aspx
- // https://web.archive.org/web/20100820152419/http://blogs.msdn.com/b/michkap/archive/2007/10/27/5717859.aspx
+ // <https://web.archive.org/web/20101004154432/http://blogs.msdn.com/b/michkap/archive/2006/04/06/569632.aspx>
+ // <https://web.archive.org/web/20100820152419/http://blogs.msdn.com/b/michkap/archive/2007/10/27/5717859.aspx>
do {
ret = ToUnicode(vkey, scancode, keys, buf, MP_ARRAY_SIZE(buf), 0);
} while (ret < 0);
@@ -356,7 +435,7 @@ static int to_unicode(UINT vkey, UINT scancode, const BYTE keys[256])
// This wraps ToUnicode to be stateless and to return only one character
// Make the buffer 10 code units long to be safe, same as here:
- // https://web.archive.org/web/20101013215215/http://blogs.msdn.com/b/michkap/archive/2006/03/24/559169.aspx
+ // <https://web.archive.org/web/20101013215215/http://blogs.msdn.com/b/michkap/archive/2006/03/24/559169.aspx>
wchar_t buf[10] = { 0 };
// Dead keys aren't useful for key shortcuts, so clear the keyboard state
@@ -428,10 +507,6 @@ static bool handle_appcommand(struct vo_w32_state *w32, UINT cmd)
static void handle_key_down(struct vo_w32_state *w32, UINT vkey, UINT scancode)
{
- // Ignore key repeat
- if (scancode & KF_REPEAT)
- return;
-
int mpkey = mp_w32_vkey_to_mpkey(vkey, scancode & KF_EXTENDED);
if (!mpkey) {
mpkey = decode_key(w32, vkey, scancode & (0xff | KF_EXTENDED));
@@ -439,7 +514,8 @@ static void handle_key_down(struct vo_w32_state *w32, UINT vkey, UINT scancode)
return;
}
- mp_input_put_key(w32->input_ctx, mpkey | mod_state(w32) | MP_KEY_STATE_DOWN);
+ int state = w32->opts->native_keyrepeat ? 0 : MP_KEY_STATE_DOWN;
+ mp_input_put_key(w32->input_ctx, mpkey | mod_state(w32) | state);
}
static void handle_key_up(struct vo_w32_state *w32, UINT vkey, UINT scancode)
@@ -456,9 +532,9 @@ static void handle_key_up(struct vo_w32_state *w32, UINT vkey, UINT scancode)
}
}
-static bool handle_char(struct vo_w32_state *w32, wchar_t wc)
+static bool handle_char(struct vo_w32_state *w32, WPARAM wc, bool decode)
{
- int c = decode_utf16(w32, wc);
+ int c = decode ? decode_utf16(w32, wc) : wc;
if (c == 0)
return true;
@@ -469,23 +545,33 @@ static bool handle_char(struct vo_w32_state *w32, wchar_t wc)
return true;
}
+static void begin_dragging(struct vo_w32_state *w32)
+{
+ if (w32->current_fs ||
+ mp_input_test_dragging(w32->input_ctx, w32->mouse_x, w32->mouse_y))
+ return;
+ // Window dragging hack
+ ReleaseCapture();
+ // The dragging model loop is entered at SendMessage() here.
+ // Unfortunately, the w32->current_fs value is stale because the
+ // input is handled in a different thread, and we cannot wait for
+ // an up-to-date value before entering the model loop if dragging
+ // needs to be kept resonsive.
+ // Workaround this by intercepting the loop in the WM_MOVING message,
+ // where the up-to-date value is available.
+ SystemParametersInfoW(SPI_GETWINARRANGING, 0, &w32->win_arranging, 0);
+ w32->dragging = true;
+ SendMessage(w32->window, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ w32->dragging = false;
+ SystemParametersInfoW(SPI_SETWINARRANGING, w32->win_arranging, 0, 0);
+
+ mp_input_put_key(w32->input_ctx, MP_INPUT_RELEASE_ALL);
+}
+
static bool handle_mouse_down(struct vo_w32_state *w32, int btn, int x, int y)
{
btn |= mod_state(w32);
mp_input_put_key(w32->input_ctx, btn | MP_KEY_STATE_DOWN);
-
- if (btn == MP_MBTN_LEFT && !w32->current_fs &&
- !mp_input_test_dragging(w32->input_ctx, x, y))
- {
- // Window dragging hack
- ReleaseCapture();
- SendMessage(w32->window, WM_NCLBUTTONDOWN, HTCAPTION, 0);
- mp_input_put_key(w32->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_UP);
-
- // Indicate the message was handled, so DefWindowProc won't be called
- return true;
- }
-
SetCapture(w32->window);
return false;
}
@@ -566,18 +652,20 @@ static double get_refresh_rate_from_gdi(const wchar_t *device)
static char *get_color_profile(void *ctx, const wchar_t *device)
{
char *name = NULL;
+ wchar_t *wname = NULL;
HDC ic = CreateICW(device, NULL, NULL, NULL);
if (!ic)
goto done;
- wchar_t wname[MAX_PATH + 1];
- if (!GetICMProfileW(ic, &(DWORD){ MAX_PATH }, wname))
+ wname = talloc_array(NULL, wchar_t, MP_PATH_MAX);
+ if (!GetICMProfileW(ic, &(DWORD){ MP_PATH_MAX - 1 }, wname))
goto done;
name = mp_to_utf8(ctx, wname);
done:
if (ic)
DeleteDC(ic);
+ talloc_free(wname);
return name;
}
@@ -603,7 +691,7 @@ static void update_dpi(struct vo_w32_state *w32)
}
w32->dpi = dpi;
- w32->dpi_scale = w32->opts->hidpi_window_scale ? w32->dpi / 96.0 : 1.0;
+ w32->dpi_scale = w32->dpi / 96.0;
signal_events(w32, VO_EVENT_DPI);
}
@@ -660,7 +748,7 @@ static void update_playback_state(struct vo_w32_state *w32)
{
struct voctrl_playback_state *pstate = &w32->current_pstate;
- if (!w32->taskbar_list3 || !w32->tbtnCreated)
+ if (!w32->taskbar_list3 || !w32->tbtn_created)
return;
if (!pstate->playing || !pstate->taskbar_progress) {
@@ -743,10 +831,10 @@ static RECT get_screen_area(struct vo_w32_state *w32)
{
// Handle --fs-screen=all
if (w32->current_fs && w32->opts->fsscreen_id == -2) {
- const int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
- const int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
- return (RECT) { x, y, x + GetSystemMetrics(SM_CXVIRTUALSCREEN),
- y + GetSystemMetrics(SM_CYVIRTUALSCREEN) };
+ const int x = get_system_metrics(w32, SM_XVIRTUALSCREEN);
+ const int y = get_system_metrics(w32, SM_YVIRTUALSCREEN);
+ return (RECT) { x, y, x + get_system_metrics(w32, SM_CXVIRTUALSCREEN),
+ y + get_system_metrics(w32, SM_CYVIRTUALSCREEN) };
}
return get_monitor_info(w32).rcMonitor;
}
@@ -842,6 +930,13 @@ static bool snap_to_screen_edges(struct vo_w32_state *w32, RECT *rc)
return true;
}
+static bool is_high_contrast(void)
+{
+ HIGHCONTRAST hc = {sizeof(hc)};
+ SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, 0);
+ return hc.dwFlags & HCF_HIGHCONTRASTON;
+}
+
static DWORD update_style(struct vo_w32_state *w32, DWORD style)
{
const DWORD NO_FRAME = WS_OVERLAPPED | WS_MINIMIZEBOX | WS_THICKFRAME;
@@ -853,17 +948,12 @@ static DWORD update_style(struct vo_w32_state *w32, DWORD style)
style |= FULLSCREEN;
} else {
style |= w32->opts->border ? FRAME : NO_FRAME;
+ if (!w32->opts->title_bar && is_high_contrast())
+ style &= ~WS_CAPTION;
}
return style;
}
-static LONG get_title_bar_height(struct vo_w32_state *w32)
-{
- RECT rc = {0};
- adjust_window_rect(w32, w32->window, &rc);
- return -rc.top;
-}
-
static void update_window_style(struct vo_w32_state *w32)
{
if (w32->parent)
@@ -1006,14 +1096,13 @@ static void update_minimized_state(struct vo_w32_state *w32)
}
}
-static void update_maximized_state(struct vo_w32_state *w32)
+static void update_maximized_state(struct vo_w32_state *w32, bool leaving_fullscreen)
{
if (w32->parent)
return;
- // Don't change the maximized state in fullscreen for now. In future, this
- // should be made to apply the maximized state on leaving fullscreen.
- if (w32->current_fs)
+ // Apply the maximized state on leaving fullscreen.
+ if (w32->current_fs && !leaving_fullscreen)
return;
WINDOWPLACEMENT wp = { .length = sizeof wp };
@@ -1065,11 +1154,24 @@ static void update_window_state(struct vo_w32_state *w32)
wr.left, wr.top, rect_w(wr), rect_h(wr),
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+ // Unmaximize the window if a size change is requested because SetWindowPos
+ // doesn't change the window maximized state.
+ // ShowWindow(SW_SHOWNOACTIVATE) can't be used here because it tries to
+ // "restore" the window to its size before it's maximized.
+ if (w32->unmaximize) {
+ WINDOWPLACEMENT wp = { .length = sizeof wp };
+ GetWindowPlacement(w32->window, &wp);
+ wp.showCmd = SW_SHOWNOACTIVATE;
+ wp.rcNormalPosition = wr;
+ SetWindowPlacement(w32->window, &wp);
+ w32->unmaximize = false;
+ }
+
// Show the window if it's not yet visible
if (!is_visible(w32->window)) {
if (w32->opts->window_minimized) {
ShowWindow(w32->window, SW_SHOWMINNOACTIVE);
- update_maximized_state(w32); // Set the WPF_RESTORETOMAXIMIZED flag
+ update_maximized_state(w32, false); // Set the WPF_RESTORETOMAXIMIZED flag
} else if (w32->opts->window_maximized) {
ShowWindow(w32->window, SW_SHOWMAXIMIZED);
} else {
@@ -1166,13 +1268,9 @@ static void update_dark_mode(const struct vo_w32_state *w32)
if (w32->api.pSetPreferredAppMode)
w32->api.pSetPreferredAppMode(1); // allow dark mode
- HIGHCONTRAST hc = {sizeof(hc)};
- SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, 0);
- bool high_contrast = hc.dwFlags & HCF_HIGHCONTRASTON;
-
// if pShouldAppsUseDarkMode is not available, just assume it to be true
- const BOOL use_dark_mode = !high_contrast && (!w32->api.pShouldAppsUseDarkMode ||
- w32->api.pShouldAppsUseDarkMode());
+ const BOOL use_dark_mode = !is_high_contrast() && (!w32->api.pShouldAppsUseDarkMode ||
+ w32->api.pShouldAppsUseDarkMode());
SetWindowTheme(w32->window, use_dark_mode ? L"DarkMode_Explorer" : L"", NULL);
@@ -1190,6 +1288,37 @@ static void update_backdrop(const struct vo_w32_state *w32)
&backdropType, sizeof(backdropType));
}
+static void update_cursor_passthrough(const struct vo_w32_state *w32)
+{
+ if (w32->parent)
+ return;
+
+ LONG_PTR exstyle = GetWindowLongPtrW(w32->window, GWL_EXSTYLE);
+ if (exstyle) {
+ if (w32->opts->cursor_passthrough) {
+ SetWindowLongPtrW(w32->window, GWL_EXSTYLE, exstyle | WS_EX_LAYERED | WS_EX_TRANSPARENT);
+ // This is required, otherwise the titlebar disappears.
+ SetLayeredWindowAttributes(w32->window, 0, 255, LWA_ALPHA);
+ } else {
+ SetWindowLongPtrW(w32->window, GWL_EXSTYLE, exstyle & ~(WS_EX_LAYERED | WS_EX_TRANSPARENT));
+ }
+ }
+}
+
+static void set_ime_conversion_mode(const struct vo_w32_state *w32, DWORD mode)
+{
+ if (w32->parent)
+ return;
+
+ HIMC imc = ImmGetContext(w32->window);
+ if (imc) {
+ DWORD sentence_mode;
+ if (ImmGetConversionStatus(imc, NULL, &sentence_mode))
+ ImmSetConversionStatus(imc, mode, sentence_mode);
+ ImmReleaseContext(w32->window, imc);
+ }
+}
+
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
@@ -1216,6 +1345,12 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
mp_dispatch_queue_process(w32->dispatch, 0);
w32->in_dispatch = false;
}
+ // Start window dragging if the flag is set by the voctrl.
+ // This is processed here to avoid blocking the dispatch queue.
+ if (w32->start_dragging) {
+ w32->start_dragging = false;
+ begin_dragging(w32);
+ }
switch (message) {
case WM_ERASEBKGND:
@@ -1241,6 +1376,18 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
case WM_MOVING: {
w32->moving = true;
RECT *rc = (RECT*)lParam;
+ // Prevent the window from being moved if the window dragging hack
+ // is active, and the window is currently in fullscreen.
+ if (w32->dragging && w32->current_fs) {
+ // Temporarily disable window arrangement to prevent aero shake
+ // from being activated. The original system setting will be restored
+ // after the dragging hack ends.
+ if (w32->win_arranging) {
+ SystemParametersInfoW(SPI_SETWINARRANGING, FALSE, 0, 0);
+ }
+ *rc = w32->windowrc;
+ return TRUE;
+ }
if (snap_to_screen_edges(w32, rc))
return TRUE;
break;
@@ -1344,6 +1491,14 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
w32->window = NULL;
PostQuitMessage(0);
break;
+ case WM_COMMAND: {
+ const char *cmd = mp_win32_menu_get_cmd(w32->menu_ctx, LOWORD(wParam));
+ if (cmd) {
+ mp_cmd_t *cmdt = mp_input_parse_cmd(w32->input_ctx, bstr0(cmd), "");
+ mp_input_queue_cmd(w32->input_ctx, cmdt);
+ }
+ break;
+ }
case WM_SYSCOMMAND:
switch (wParam & 0xFFF0) {
case SC_SCREENSAVE:
@@ -1404,9 +1559,16 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
break;
case WM_CHAR:
case WM_SYSCHAR:
- if (handle_char(w32, wParam))
+ if (handle_char(w32, wParam, true))
return 0;
break;
+ case WM_UNICHAR:
+ if (wParam == UNICODE_NOCHAR) {
+ return TRUE;
+ } else if (handle_char(w32, wParam, false)) {
+ return 0;
+ }
+ break;
case WM_KILLFOCUS:
mp_input_put_key(w32->input_ctx, MP_INPUT_RELEASE_ALL);
w32->focused = false;
@@ -1431,12 +1593,13 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
break;
case WM_MOUSEMOVE: {
if (!w32->tracking) {
- w32->tracking = TrackMouseEvent(&w32->trackEvent);
+ w32->tracking = TrackMouseEvent(&w32->track_event);
mp_input_put_key(w32->input_ctx, MP_KEY_MOUSE_ENTER);
}
// Windows can send spurious mouse events, which would make the mpv
// core unhide the mouse cursor on completely unrelated events. See:
- // https://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
+ // <https://web.archive.org/web/20100821161603/
+ // https://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx>
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
if (x != w32->mouse_x || y != w32->mouse_y) {
@@ -1480,16 +1643,18 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
handle_mouse_down(w32,
HIWORD(wParam) == 1 ? MP_MBTN_BACK : MP_MBTN_FORWARD,
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
- break;
+ return TRUE;
case WM_XBUTTONUP:
handle_mouse_up(w32,
HIWORD(wParam) == 1 ? MP_MBTN_BACK : MP_MBTN_FORWARD);
- break;
+ return TRUE;
case WM_DISPLAYCHANGE:
force_update_display_info(w32);
break;
case WM_SETTINGCHANGE:
update_dark_mode(w32);
+ update_window_style(w32);
+ update_window_state(w32);
break;
case WM_NCCALCSIZE:
if (!w32->opts->border)
@@ -1497,15 +1662,51 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
// Apparently removing WS_CAPTION disables some window animation, instead
// just reduce non-client size to remove title bar.
if (wParam && lParam && w32->opts->border && !w32->opts->title_bar &&
- !w32->current_fs && !w32->parent)
+ !w32->current_fs && !w32->parent &&
+ (GetWindowLongPtrW(w32->window, GWL_STYLE) & WS_CAPTION))
{
- ((LPNCCALCSIZE_PARAMS) lParam)->rgrc[0].top -= get_title_bar_height(w32);
+ // Remove all NC area on Windows 10 due to inability to control the
+ // top bar height before Windows 11.
+ if (!check_windows10_build(22000) && !IsMaximized(w32->window))
+ return 0;
+ RECT r = {0};
+ adjust_window_rect(w32, w32->window, &r);
+ NCCALCSIZE_PARAMS *p = (LPNCCALCSIZE_PARAMS)lParam;
+ p->rgrc[0].top += r.top + get_title_bar_height(w32);
+ }
+ break;
+ case WM_IME_STARTCOMPOSITION: {
+ HIMC imc = ImmGetContext(w32->window);
+ if (imc) {
+ COMPOSITIONFORM cf = {.dwStyle = CFS_POINT, .ptCurrentPos = {0, 0}};
+ ImmSetCompositionWindow(imc, &cf);
+ ImmReleaseContext(w32->window, imc);
}
break;
}
+ case WM_CREATE:
+ // The IME can only be changed to alphanumeric input after it's initialized.
+ // Unfortunately, there is no way to know when this happens, as
+ // none of the WM_CREATE, WM_INPUTLANGCHANGE, or WM_IME_* messages work.
+ // This works if the IME is initialized within a short time after
+ // the window is created. Otherwise, fallback to setting alphanumeric mode on
+ // the first keypress.
+ SetTimer(w32->window, (UINT_PTR)WM_CREATE, 250, NULL);
+ break;
+ case WM_TIMER:
+ if (wParam == WM_CREATE) {
+ // Default to alphanumeric input when the IME is first initialized.
+ set_ime_conversion_mode(w32, IME_CMODE_ALPHANUMERIC);
+ return 0;
+ }
+ break;
+ case WM_SHOWMENU:
+ mp_win32_menu_show(w32->menu_ctx, w32->window);
+ break;
+ }
- if (message == w32->tbtnCreatedMsg) {
- w32->tbtnCreated = true;
+ if (message == w32->tbtn_created_msg) {
+ w32->tbtn_created = true;
update_playback_state(w32);
return 0;
}
@@ -1609,13 +1810,31 @@ static void remove_parent_hook(struct vo_w32_state *w32)
UnhookWinEvent(w32->parent_evt_hook);
}
+static bool is_key_message(UINT msg)
+{
+ return msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN ||
+ msg == WM_KEYUP || msg == WM_SYSKEYUP;
+}
+
// Dispatch incoming window events and handle them.
// This returns only when the thread is asked to terminate.
static void run_message_loop(struct vo_w32_state *w32)
{
MSG msg;
- while (GetMessageW(&msg, 0, 0, 0) > 0)
+ while (!w32->destroyed && GetMessageW(&msg, 0, 0, 0) > 0) {
+ // Change the conversion mode on the first keypress, in case the timer
+ // solution fails. Note that this leaves the mode indicator in the language
+ // bar showing the original mode until a key is pressed.
+ if (is_key_message(msg.message) && !w32->conversion_mode_init) {
+ set_ime_conversion_mode(w32, IME_CMODE_ALPHANUMERIC);
+ w32->conversion_mode_init = true;
+ KillTimer(w32->window, (UINT_PTR)WM_CREATE);
+ }
+ // Only send IME messages to TranslateMessage
+ if (is_key_message(msg.message) && msg.wParam == VK_PROCESSKEY)
+ TranslateMessage(&msg);
DispatchMessageW(&msg);
+ }
// Even if the message loop somehow exits, we still have to respond to
// external requests until termination is requested.
@@ -1623,9 +1842,8 @@ static void run_message_loop(struct vo_w32_state *w32)
mp_dispatch_queue_process(w32->dispatch, 1000);
}
-static void gui_thread_reconfig(void *ptr)
+static void window_reconfig(struct vo_w32_state *w32, bool force)
{
- struct vo_w32_state *w32 = ptr;
struct vo *vo = w32->vo;
RECT r = get_working_area(w32);
@@ -1648,14 +1866,14 @@ static void gui_thread_reconfig(void *ptr)
vo_calc_window_geometry3(vo, &screen, &mon, w32->dpi_scale, &geo);
vo_apply_window_geometry(vo, &geo);
- bool reset_size = (w32->o_dwidth != vo->dwidth ||
+ bool reset_size = ((w32->o_dwidth != vo->dwidth ||
w32->o_dheight != vo->dheight) &&
- w32->opts->auto_window_resize;
+ w32->opts->auto_window_resize) || force;
w32->o_dwidth = vo->dwidth;
w32->o_dheight = vo->dheight;
- if (!w32->parent && !w32->window_bounds_initialized) {
+ if (!w32->parent && (!w32->window_bounds_initialized || force)) {
SetRect(&w32->windowrc, geo.win.x0, geo.win.y0,
geo.win.x0 + vo->dwidth, geo.win.y0 + vo->dheight);
w32->prev_windowrc = w32->windowrc;
@@ -1686,6 +1904,11 @@ finish:
reinit_window_state(w32);
}
+static void gui_thread_reconfig(void *ptr)
+{
+ window_reconfig(ptr, false);
+}
+
// Resize the window. On the first call, it's also made visible.
void vo_w32_config(struct vo *vo)
{
@@ -1704,26 +1927,15 @@ static void w32_api_load(struct vo_w32_state *w32)
// Available since Win10
w32->api.pAdjustWindowRectExForDpi = !user32_dll ? NULL :
(void *)GetProcAddress(user32_dll, "AdjustWindowRectExForDpi");
-
- // imm32.dll must be loaded dynamically
- // to account for machines without East Asian language support
- HMODULE imm32_dll = LoadLibraryW(L"imm32.dll");
- w32->api.pImmDisableIME = !imm32_dll ? NULL :
- (void *)GetProcAddress(imm32_dll, "ImmDisableIME");
+ w32->api.pGetSystemMetricsForDpi = !user32_dll ? NULL :
+ (void *)GetProcAddress(user32_dll, "GetSystemMetricsForDpi");
// Dark mode related functions, available since the 1809 Windows 10 update
// Check the Windows build version as on previous versions used ordinals
// may point to unexpected code/data. Alternatively could check uxtheme.dll
// version directly, but it is little bit more boilerplate code, and build
// number is good enough check.
- void (WINAPI *pRtlGetNtVersionNumbers)(LPDWORD, LPDWORD, LPDWORD) =
- (void *)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetNtVersionNumbers");
-
- DWORD major, build;
- pRtlGetNtVersionNumbers(&major, NULL, &build);
- build &= ~0xF0000000;
-
- HMODULE uxtheme_dll = (major < 10 || build < 17763) ? NULL :
+ HMODULE uxtheme_dll = !check_windows10_build(17763) ? NULL :
GetModuleHandle(L"uxtheme.dll");
w32->api.pShouldAppsUseDarkMode = !uxtheme_dll ? NULL :
(void *)GetProcAddress(uxtheme_dll, MAKEINTRESOURCEA(132));
@@ -1741,10 +1953,6 @@ static MP_THREAD_VOID gui_thread(void *ptr)
w32_api_load(w32);
- // Disables the IME for windows on this thread
- if (w32->api.pImmDisableIME)
- w32->api.pImmDisableIME(0);
-
if (w32->opts->WinID >= 0)
w32->parent = (HWND)(intptr_t)(w32->opts->WinID);
@@ -1776,6 +1984,8 @@ static MP_THREAD_VOID gui_thread(void *ptr)
update_affinity(w32);
if (w32->opts->backdrop_type)
update_backdrop(w32);
+ if (w32->opts->cursor_passthrough)
+ update_cursor_passthrough(w32);
if (SUCCEEDED(OleInitialize(NULL))) {
ole_ok = true;
@@ -1805,7 +2015,7 @@ static MP_THREAD_VOID gui_thread(void *ptr)
ITaskbarList3_Release(w32->taskbar_list3);
w32->taskbar_list3 = NULL;
} else {
- w32->tbtnCreatedMsg = RegisterWindowMessage(L"TaskbarButtonCreated");
+ w32->tbtn_created_msg = RegisterWindowMessage(L"TaskbarButtonCreated");
}
}
} else {
@@ -1813,7 +2023,7 @@ static MP_THREAD_VOID gui_thread(void *ptr)
}
w32->tracking = FALSE;
- w32->trackEvent = (TRACKMOUSEEVENT){
+ w32->track_event = (TRACKMOUSEEVENT){
.cbSize = sizeof(TRACKMOUSEEVENT),
.dwFlags = TME_LEAVE,
.hwndTrack = w32->window,
@@ -1866,6 +2076,7 @@ bool vo_w32_init(struct vo *vo)
.dispatch = mp_dispatch_create(w32),
};
w32->opts = w32->opts_cache->opts;
+ w32->menu_ctx = mp_win32_menu_init();
vo->w32 = w32;
if (mp_thread_create(&w32->thread, gui_thread, w32))
@@ -1949,6 +2160,8 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
struct mp_vo_opts *vo_opts = w32->opts_cache->opts;
if (changed_option == &vo_opts->fullscreen) {
+ if (!vo_opts->fullscreen)
+ update_maximized_state(w32, true);
reinit_window_state(w32);
} else if (changed_option == &vo_opts->window_affinity) {
update_affinity(w32);
@@ -1956,6 +2169,8 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
update_window_state(w32);
} else if (changed_option == &vo_opts->backdrop_type) {
update_backdrop(w32);
+ } else if (changed_option == &vo_opts->cursor_passthrough) {
+ update_cursor_passthrough(w32);
} else if (changed_option == &vo_opts->border ||
changed_option == &vo_opts->title_bar)
{
@@ -1964,9 +2179,16 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
} else if (changed_option == &vo_opts->window_minimized) {
update_minimized_state(w32);
} else if (changed_option == &vo_opts->window_maximized) {
- update_maximized_state(w32);
+ update_maximized_state(w32, false);
} else if (changed_option == &vo_opts->window_corners) {
update_corners_pref(w32);
+ } else if (changed_option == &vo_opts->geometry || changed_option == &vo_opts->autofit ||
+ changed_option == &vo_opts->autofit_smaller || changed_option == &vo_opts->autofit_larger)
+ {
+ if (w32->opts->window_maximized) {
+ w32->unmaximize = true;
+ }
+ window_reconfig(w32, true);
}
}
@@ -1989,8 +2211,8 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
return VO_FALSE;
RECT *rc = w32->current_fs ? &w32->prev_windowrc : &w32->windowrc;
- s[0] = rect_w(*rc) / w32->dpi_scale;
- s[1] = rect_h(*rc) / w32->dpi_scale;
+ s[0] = rect_w(*rc);
+ s[1] = rect_h(*rc);
return VO_TRUE;
}
case VOCTRL_SET_UNFS_WINDOW_SIZE: {
@@ -1999,12 +2221,12 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
if (!w32->window_bounds_initialized)
return VO_FALSE;
- s[0] *= w32->dpi_scale;
- s[1] *= w32->dpi_scale;
-
RECT *rc = w32->current_fs ? &w32->prev_windowrc : &w32->windowrc;
resize_and_move_rect(w32, rc, s[0], s[1]);
+ if (w32->opts->window_maximized) {
+ w32->unmaximize = true;
+ }
w32->fit_on_screen = true;
reinit_window_state(w32);
return VO_TRUE;
@@ -2064,6 +2286,15 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
case VOCTRL_GET_FOCUSED:
*(bool *)arg = w32->focused;
return VO_TRUE;
+ case VOCTRL_BEGIN_DRAGGING:
+ w32->start_dragging = true;
+ return VO_TRUE;
+ case VOCTRL_SHOW_MENU:
+ PostMessageW(w32->window, WM_SHOWMENU, 0, 0);
+ return VO_TRUE;
+ case VOCTRL_UPDATE_MENU:
+ mp_win32_menu_update(w32->menu_ctx, (struct mpv_node *)arg);
+ return VO_TRUE;
}
return VO_NOTIMPL;
}
@@ -2127,6 +2358,7 @@ void vo_w32_uninit(struct vo *vo)
AvRevertMmThreadCharacteristics(w32->avrt_handle);
+ mp_win32_menu_uninit(w32->menu_ctx);
talloc_free(w32);
vo->w32 = NULL;
}
@@ -2142,3 +2374,24 @@ void vo_w32_run_on_thread(struct vo *vo, void (*cb)(void *ctx), void *ctx)
struct vo_w32_state *w32 = vo->w32;
mp_dispatch_run(w32->dispatch, cb, ctx);
}
+
+void vo_w32_set_transparency(struct vo *vo, bool enable)
+{
+ struct vo_w32_state *w32 = vo->w32;
+ if (w32->parent)
+ return;
+
+ DWM_BLURBEHIND dbb = {0};
+ if (enable) {
+ HRGN rgn = CreateRectRgn(0, 0, -1, -1);
+ dbb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
+ dbb.hRgnBlur = rgn;
+ dbb.fEnable = TRUE;
+ DwmEnableBlurBehindWindow(w32->window, &dbb);
+ DeleteObject(rgn);
+ } else {
+ dbb.dwFlags = DWM_BB_ENABLE;
+ dbb.fEnable = FALSE;
+ DwmEnableBlurBehindWindow(w32->window, &dbb);
+ }
+}
diff --git a/video/out/w32_common.h b/video/out/w32_common.h
index 528b216..bc22a2d 100644
--- a/video/out/w32_common.h
+++ b/video/out/w32_common.h
@@ -32,5 +32,6 @@ int vo_w32_control(struct vo *vo, int *events, int request, void *arg);
void vo_w32_config(struct vo *vo);
HWND vo_w32_hwnd(struct vo *vo);
void vo_w32_run_on_thread(struct vo *vo, void (*cb)(void *ctx), void *ctx);
+void vo_w32_set_transparency(struct vo *vo, bool enable);
#endif /* MPLAYER_W32_COMMON_H */
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index 589135f..4a86c21 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -56,6 +56,10 @@
#include "cursor-shape-v1.h"
#endif
+#if WAYLAND_VERSION_MAJOR > 1 || WAYLAND_VERSION_MINOR >= 21
+#define HAVE_WAYLAND_1_21
+#endif
+
#if WAYLAND_VERSION_MAJOR > 1 || WAYLAND_VERSION_MINOR >= 22
#define HAVE_WAYLAND_1_22
#endif
@@ -133,7 +137,8 @@ static const struct mp_keymap keymap[] = {
{XKB_KEY_XF86HomePage, MP_KEY_HOMEPAGE}, {XKB_KEY_XF86WWW, MP_KEY_WWW},
{XKB_KEY_XF86Mail, MP_KEY_MAIL}, {XKB_KEY_XF86Favorites, MP_KEY_FAVORITES},
{XKB_KEY_XF86Search, MP_KEY_SEARCH}, {XKB_KEY_XF86Sleep, MP_KEY_SLEEP},
- {XKB_KEY_XF86Back, MP_KEY_BACK}, {XKB_KEY_XF86Tools, MP_KEY_TOOLS},
+ {XKB_KEY_XF86Back, MP_KEY_GO_BACK}, {XKB_KEY_XF86Forward, MP_KEY_GO_FORWARD},
+ {XKB_KEY_XF86Tools, MP_KEY_TOOLS},
{XKB_KEY_XF86ZoomIn, MP_KEY_ZOOMIN}, {XKB_KEY_XF86ZoomOut, MP_KEY_ZOOMOUT},
{0, 0}
@@ -182,53 +187,93 @@ struct vo_wayland_output {
struct wl_list link;
};
+struct vo_wayland_seat {
+ struct vo_wayland_state *wl;
+ struct wl_seat *seat;
+ uint32_t id;
+ struct wl_keyboard *keyboard;
+ struct wl_pointer *pointer;
+ struct wl_touch *touch;
+ struct wl_data_device *dnd_ddev;
+ /* TODO: unvoid this if required wayland protocols is bumped to 1.32+ */
+ void *cursor_shape_device;
+ uint32_t pointer_enter_serial;
+ uint32_t pointer_button_serial;
+ struct xkb_keymap *xkb_keymap;
+ struct xkb_state *xkb_state;
+ uint32_t keyboard_code;
+ int mpkey;
+ int mpmod;
+ double axis_value_vertical;
+ int32_t axis_value120_vertical;
+ double axis_value_horizontal;
+ int32_t axis_value120_horizontal;
+ bool axis_value120_scroll;
+ bool has_keyboard_input;
+ struct wl_list link;
+};
+
+static bool single_output_spanned(struct vo_wayland_state *wl);
+
static int check_for_resize(struct vo_wayland_state *wl, int edge_pixels,
enum xdg_toplevel_resize_edge *edge);
-static int get_mods(struct vo_wayland_state *wl);
+static int get_mods(struct vo_wayland_seat *seat);
+static int greatest_common_divisor(int a, int b);
static int lookupkey(int key);
-static int set_cursor_visibility(struct vo_wayland_state *wl, bool on);
+static int set_cursor_visibility(struct vo_wayland_seat *s, bool on);
static int spawn_cursor(struct vo_wayland_state *wl);
static void add_feedback(struct vo_wayland_feedback_pool *fback_pool,
struct wp_presentation_feedback *fback);
-static void get_shape_device(struct vo_wayland_state *wl);
-static int greatest_common_divisor(int a, int b);
+static void apply_keepaspect(struct vo_wayland_state *wl, int *width, int *height);
+static void get_shape_device(struct vo_wayland_state *wl, struct vo_wayland_seat *s);
static void guess_focus(struct vo_wayland_state *wl);
-static void prepare_resize(struct vo_wayland_state *wl, int width, int height);
+static void prepare_resize(struct vo_wayland_state *wl);
static void remove_feedback(struct vo_wayland_feedback_pool *fback_pool,
struct wp_presentation_feedback *fback);
static void remove_output(struct vo_wayland_output *out);
+static void remove_seat(struct vo_wayland_seat *seat);
static void request_decoration_mode(struct vo_wayland_state *wl, uint32_t mode);
static void rescale_geometry(struct vo_wayland_state *wl, double old_scale);
static void set_geometry(struct vo_wayland_state *wl, bool resize);
static void set_surface_scaling(struct vo_wayland_state *wl);
-static void window_move(struct vo_wayland_state *wl, uint32_t serial);
+static void update_output_scaling(struct vo_wayland_state *wl);
+static void update_output_geometry(struct vo_wayland_state *wl, struct mp_rect old_geometry,
+ struct mp_rect old_output_geometry);
/* Wayland listener boilerplate */
static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t sx, wl_fixed_t sy)
{
- struct vo_wayland_state *wl = data;
-
- wl->pointer = pointer;
- wl->pointer_id = serial;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
- set_cursor_visibility(wl, wl->cursor_visible);
+ s->pointer_enter_serial = serial;
+ set_cursor_visibility(s, wl->cursor_visible);
mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_ENTER);
+
+ wl->mouse_x = wl_fixed_to_int(sx) * wl->scaling;
+ wl->mouse_y = wl_fixed_to_int(sy) * wl->scaling;
+
+ if (!wl->toplevel_configured)
+ mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y);
+ wl->toplevel_configured = false;
}
static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
uint32_t serial, struct wl_surface *surface)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_LEAVE);
}
static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
wl->mouse_x = wl_fixed_to_int(sx) * wl->scaling;
wl->mouse_y = wl_fixed_to_int(sy) * wl->scaling;
@@ -242,7 +287,8 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, uint32_t time, uint32_t button,
uint32_t state)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
state = state == WL_POINTER_BUTTON_STATE_PRESSED ? MP_KEY_STATE_DOWN
: MP_KEY_STATE_UP;
@@ -272,44 +318,106 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
}
if (button)
- mp_input_put_key(wl->vo->input_ctx, button | state | wl->mpmod);
+ mp_input_put_key(wl->vo->input_ctx, button | state | s->mpmod);
+ enum xdg_toplevel_resize_edge edges;
if (!mp_input_test_dragging(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y) &&
- !wl->locked_size && (button == MP_MBTN_LEFT) && (state == MP_KEY_STATE_DOWN))
+ !wl->locked_size && (button == MP_MBTN_LEFT) && (state == MP_KEY_STATE_DOWN) &&
+ !wl->vo_opts->border && check_for_resize(wl, wl->opts->edge_pixels_pointer, &edges))
{
- uint32_t edges;
// Implement an edge resize zone if there are no decorations
- if (!wl->vo_opts->border && check_for_resize(wl, wl->opts->edge_pixels_pointer, &edges)) {
- xdg_toplevel_resize(wl->xdg_toplevel, wl->seat, serial, edges);
- } else {
- window_move(wl, serial);
- }
- // Explicitly send an UP event after the client finishes a move/resize
+ xdg_toplevel_resize(wl->xdg_toplevel, s->seat, serial, edges);
+ // Explicitly send an UP event after the client finishes a resize
mp_input_put_key(wl->vo->input_ctx, button | MP_KEY_STATE_UP);
+ } else if (state == MP_KEY_STATE_DOWN) {
+ // Save the serial and seat for voctrl-initialized dragging requests.
+ s->pointer_button_serial = serial;
+ wl->last_button_seat = s;
+ } else {
+ wl->last_button_seat = NULL;
}
}
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ switch (axis) {
+ case WL_POINTER_AXIS_VERTICAL_SCROLL:
+ s->axis_value_vertical += wl_fixed_to_double(value);
+ break;
+ case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
+ s->axis_value_horizontal += wl_fixed_to_double(value);
+ break;
+ }
+}
+
+static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer)
+{
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
+ double value_vertical, value_horizontal;
+ if (s->axis_value120_scroll) {
+ // Prefer axis_value120 if supported and the axis event is from mouse wheel.
+ value_vertical = s->axis_value120_vertical / 120.0;
+ value_horizontal = s->axis_value120_horizontal / 120.0;
+ } else {
+ // The axis value is specified in logical coordinates, but the exact value emitted
+ // by one mouse wheel click is unspecified. In practice, most compositors use either
+ // 10 (GNOME, Weston) or 15 (wlroots, same as libinput) as the value.
+ // Divide the value by 10 and clamp it between -1 and 1 so that mouse wheel clicks
+ // work as intended on all compositors while still allowing high resolution trackpads.
+ value_vertical = MPCLAMP(s->axis_value_vertical / 10.0, -1, 1);
+ value_horizontal = MPCLAMP(s->axis_value_horizontal / 10.0, -1, 1);
+ }
+
+ if (value_vertical > 0)
+ mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_DOWN | s->mpmod, +value_vertical);
+ if (value_vertical < 0)
+ mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_UP | s->mpmod, -value_vertical);
+ if (value_horizontal > 0)
+ mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_RIGHT | s->mpmod, +value_horizontal);
+ if (value_horizontal < 0)
+ mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_LEFT | s->mpmod, -value_horizontal);
+
+ s->axis_value120_scroll = false;
+ s->axis_value_vertical = 0;
+ s->axis_value_horizontal = 0;
+ s->axis_value120_vertical = 0;
+ s->axis_value120_horizontal = 0;
+}
+
+static void pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer,
+ uint32_t axis_source)
+{
+}
+
+static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis)
+{
+}
+
+static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer,
+ uint32_t axis, int32_t discrete)
+{
+}
- double val = wl_fixed_to_double(value) < 0 ? -1 : 1;
+#ifdef HAVE_WAYLAND_1_21
+static void pointer_handle_axis_value120(void *data, struct wl_pointer *wl_pointer,
+ uint32_t axis, int32_t value120)
+{
+ struct vo_wayland_seat *s = data;
+ s->axis_value120_scroll = true;
switch (axis) {
case WL_POINTER_AXIS_VERTICAL_SCROLL:
- if (value > 0)
- mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_DOWN | wl->mpmod, +val);
- if (value < 0)
- mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_UP | wl->mpmod, -val);
+ s->axis_value120_vertical += value120;
break;
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
- if (value > 0)
- mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_RIGHT | wl->mpmod, +val);
- if (value < 0)
- mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_LEFT | wl->mpmod, -val);
+ s->axis_value120_horizontal += value120;
break;
}
}
+#endif
static const struct wl_pointer_listener pointer_listener = {
pointer_handle_enter,
@@ -317,40 +425,55 @@ static const struct wl_pointer_listener pointer_listener = {
pointer_handle_motion,
pointer_handle_button,
pointer_handle_axis,
+ pointer_handle_frame,
+ pointer_handle_axis_source,
+ pointer_handle_axis_stop,
+ pointer_handle_axis_discrete,
+#ifdef HAVE_WAYLAND_1_21
+ pointer_handle_axis_value120,
+#endif
};
static void touch_handle_down(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, struct wl_surface *surface,
int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
wl->mouse_x = wl_fixed_to_int(x_w) * wl->scaling;
wl->mouse_y = wl_fixed_to_int(y_w) * wl->scaling;
- enum xdg_toplevel_resize_edge edge;
- if (!mp_input_test_dragging(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y)) {
- if (check_for_resize(wl, wl->opts->edge_pixels_touch, &edge)) {
- xdg_toplevel_resize(wl->xdg_toplevel, wl->seat, serial, edge);
- } else {
- xdg_toplevel_move(wl->xdg_toplevel, wl->seat, serial);
- }
- }
-
mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y);
mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_DOWN);
+
+ enum xdg_toplevel_resize_edge edge;
+ if (!mp_input_test_dragging(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y) &&
+ !wl->locked_size && check_for_resize(wl, wl->opts->edge_pixels_touch, &edge))
+ {
+ xdg_toplevel_resize(wl->xdg_toplevel, s->seat, serial, edge);
+ // Explicitly send an UP event after the client finishes a resize
+ mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_UP);
+ } else {
+ // Save the serial and seat for voctrl-initialized dragging requests.
+ s->pointer_button_serial = serial;
+ wl->last_button_seat = s;
+ }
}
static void touch_handle_up(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, int32_t id)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_UP);
+ wl->last_button_seat = NULL;
}
static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
wl->mouse_x = wl_fixed_to_int(x_w) * wl->scaling;
wl->mouse_y = wl_fixed_to_int(y_w) * wl->scaling;
@@ -366,18 +489,31 @@ static void touch_handle_cancel(void *data, struct wl_touch *wl_touch)
{
}
+static void touch_handle_shape(void *data, struct wl_touch *wl_touch,
+ int32_t id, wl_fixed_t major, wl_fixed_t minor)
+{
+}
+
+static void touch_handle_orientation(void *data, struct wl_touch *wl_touch,
+ int32_t id, wl_fixed_t orientation)
+{
+}
+
static const struct wl_touch_listener touch_listener = {
touch_handle_down,
touch_handle_up,
touch_handle_motion,
touch_handle_frame,
touch_handle_cancel,
+ touch_handle_shape,
+ touch_handle_orientation,
};
static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
uint32_t format, int32_t fd, uint32_t size)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
char *map_str;
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
@@ -391,23 +527,25 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
return;
}
- wl->xkb_keymap = xkb_keymap_new_from_buffer(wl->xkb_context, map_str,
- strnlen(map_str, size),
- XKB_KEYMAP_FORMAT_TEXT_V1, 0);
+ if (!s->xkb_keymap)
+ s->xkb_keymap = xkb_keymap_new_from_buffer(wl->xkb_context, map_str,
+ strnlen(map_str, size),
+ XKB_KEYMAP_FORMAT_TEXT_V1, 0);
munmap(map_str, size);
close(fd);
- if (!wl->xkb_keymap) {
+ if (!s->xkb_keymap) {
MP_ERR(wl, "failed to compile keymap\n");
return;
}
- wl->xkb_state = xkb_state_new(wl->xkb_keymap);
- if (!wl->xkb_state) {
+ if (!s->xkb_state)
+ s->xkb_state = xkb_state_new(s->xkb_keymap);
+ if (!s->xkb_state) {
MP_ERR(wl, "failed to create XKB state\n");
- xkb_keymap_unref(wl->xkb_keymap);
- wl->xkb_keymap = NULL;
+ xkb_keymap_unref(s->xkb_keymap);
+ s->xkb_keymap = NULL;
return;
}
}
@@ -416,19 +554,21 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface,
struct wl_array *keys)
{
- struct vo_wayland_state *wl = data;
- wl->has_keyboard_input = true;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
+ s->has_keyboard_input = true;
guess_focus(wl);
}
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface)
{
- struct vo_wayland_state *wl = data;
- wl->has_keyboard_input = false;
- wl->keyboard_code = 0;
- wl->mpkey = 0;
- wl->mpmod = 0;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
+ s->has_keyboard_input = false;
+ s->keyboard_code = 0;
+ s->mpkey = 0;
+ s->mpmod = 0;
mp_input_put_key(wl->vo->input_ctx, MP_INPUT_RELEASE_ALL);
guess_focus(wl);
}
@@ -437,30 +577,37 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t time, uint32_t key,
uint32_t state)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
- wl->keyboard_code = key + 8;
- xkb_keysym_t sym = xkb_state_key_get_one_sym(wl->xkb_state, wl->keyboard_code);
+ s->keyboard_code = key + 8;
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(s->xkb_state, s->keyboard_code);
int mpkey = lookupkey(sym);
state = state == WL_KEYBOARD_KEY_STATE_PRESSED ? MP_KEY_STATE_DOWN
: MP_KEY_STATE_UP;
if (mpkey) {
- mp_input_put_key(wl->vo->input_ctx, mpkey | state | wl->mpmod);
+ mp_input_put_key(wl->vo->input_ctx, mpkey | state | s->mpmod);
} else {
- char s[128];
- if (xkb_keysym_to_utf8(sym, s, sizeof(s)) > 0) {
- mp_input_put_key_utf8(wl->vo->input_ctx, state | wl->mpmod, bstr0(s));
+ char str[128];
+ if (xkb_keysym_to_utf8(sym, str, sizeof(str)) > 0) {
+ mp_input_put_key_utf8(wl->vo->input_ctx, state | s->mpmod, bstr0(str));
} else {
// Assume a modifier was pressed and handle it in the mod event instead.
+ // If a modifier is released before a regular key, also release that
+ // key to not activate it again by accident.
+ if (state == MP_KEY_STATE_UP) {
+ s->mpkey = 0;
+ mp_input_put_key(wl->vo->input_ctx, MP_INPUT_RELEASE_ALL);
+ }
return;
}
}
if (state == MP_KEY_STATE_DOWN)
- wl->mpkey = mpkey;
+ s->mpkey = mpkey;
if (mpkey && state == MP_KEY_STATE_UP)
- wl->mpkey = 0;
+ s->mpkey = 0;
}
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
@@ -468,21 +615,23 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar
uint32_t mods_latched, uint32_t mods_locked,
uint32_t group)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
- if (wl->xkb_state) {
- xkb_state_update_mask(wl->xkb_state, mods_depressed, mods_latched,
+ if (s->xkb_state) {
+ xkb_state_update_mask(s->xkb_state, mods_depressed, mods_latched,
mods_locked, 0, 0, group);
- wl->mpmod = get_mods(wl);
- if (wl->mpkey)
- mp_input_put_key(wl->vo->input_ctx, wl->mpkey | MP_KEY_STATE_DOWN | wl->mpmod);
+ s->mpmod = get_mods(s);
+ if (s->mpkey)
+ mp_input_put_key(wl->vo->input_ctx, s->mpkey | MP_KEY_STATE_DOWN | s->mpmod);
}
}
static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
int32_t rate, int32_t delay)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
if (wl->vo_opts->native_keyrepeat)
mp_input_set_repeat_info(wl->vo->input_ctx, rate, delay);
}
@@ -499,43 +648,50 @@ static const struct wl_keyboard_listener keyboard_listener = {
static void seat_handle_caps(void *data, struct wl_seat *seat,
enum wl_seat_capability caps)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
- if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wl->pointer) {
- wl->pointer = wl_seat_get_pointer(seat);
- get_shape_device(wl);
- wl_pointer_add_listener(wl->pointer, &pointer_listener, wl);
- } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && wl->pointer) {
- wl_pointer_destroy(wl->pointer);
- wl->pointer = NULL;
+ if ((caps & WL_SEAT_CAPABILITY_POINTER) && !s->pointer) {
+ s->pointer = wl_seat_get_pointer(seat);
+ get_shape_device(s->wl, s);
+ wl_pointer_add_listener(s->pointer, &pointer_listener, s);
+ } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && s->pointer) {
+ wl_pointer_destroy(s->pointer);
+ s->pointer = NULL;
}
- if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl->keyboard) {
- wl->keyboard = wl_seat_get_keyboard(seat);
- wl_keyboard_add_listener(wl->keyboard, &keyboard_listener, wl);
- } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl->keyboard) {
- wl_keyboard_destroy(wl->keyboard);
- wl->keyboard = NULL;
+ if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !s->keyboard) {
+ s->keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_add_listener(s->keyboard, &keyboard_listener, s);
+ } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && s->keyboard) {
+ wl_keyboard_destroy(s->keyboard);
+ s->keyboard = NULL;
}
- if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !wl->touch) {
- wl->touch = wl_seat_get_touch(seat);
- wl_touch_set_user_data(wl->touch, wl);
- wl_touch_add_listener(wl->touch, &touch_listener, wl);
- } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && wl->touch) {
- wl_touch_destroy(wl->touch);
- wl->touch = NULL;
+ if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !s->touch) {
+ s->touch = wl_seat_get_touch(seat);
+ wl_touch_set_user_data(s->touch, s);
+ wl_touch_add_listener(s->touch, &touch_listener, s);
+ } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && s->touch) {
+ wl_touch_destroy(s->touch);
+ s->touch = NULL;
}
}
+static void seat_handle_name(void *data, struct wl_seat *seat,
+ const char *name)
+{
+}
+
static const struct wl_seat_listener seat_listener = {
seat_handle_caps,
+ seat_handle_name,
};
static void data_offer_handle_offer(void *data, struct wl_data_offer *offer,
const char *mime_type)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
int score = mp_event_get_mime_type_score(wl->vo->input_ctx, mime_type);
if (score > wl->dnd_mime_score && wl->vo_opts->drag_and_drop != -2) {
wl->dnd_mime_score = score;
@@ -552,7 +708,8 @@ static void data_offer_source_actions(void *data, struct wl_data_offer *offer, u
static void data_offer_action(void *data, struct wl_data_offer *wl_data_offer, uint32_t dnd_action)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
if (dnd_action && wl->vo_opts->drag_and_drop != -2) {
if (wl->vo_opts->drag_and_drop >= 0) {
wl->dnd_action = wl->vo_opts->drag_and_drop;
@@ -560,8 +717,14 @@ static void data_offer_action(void *data, struct wl_data_offer *wl_data_offer, u
wl->dnd_action = dnd_action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY ?
DND_REPLACE : DND_APPEND;
}
- MP_VERBOSE(wl, "DND action is %s\n",
- wl->dnd_action == DND_REPLACE ? "DND_REPLACE" : "DND_APPEND");
+
+ static const char * const dnd_action_names[] = {
+ [DND_REPLACE] = "DND_REPLACE",
+ [DND_APPEND] = "DND_APPEND",
+ [DND_INSERT_NEXT] = "DND_INSERT_NEXT",
+ };
+
+ MP_VERBOSE(wl, "DND action is %s\n", dnd_action_names[wl->dnd_action]);
}
}
@@ -574,12 +737,13 @@ static const struct wl_data_offer_listener data_offer_listener = {
static void data_device_handle_data_offer(void *data, struct wl_data_device *wl_ddev,
struct wl_data_offer *id)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
if (wl->dnd_offer)
wl_data_offer_destroy(wl->dnd_offer);
wl->dnd_offer = id;
- wl_data_offer_add_listener(id, &data_offer_listener, wl);
+ wl_data_offer_add_listener(id, &data_offer_listener, s);
}
static void data_device_handle_enter(void *data, struct wl_data_device *wl_ddev,
@@ -587,7 +751,8 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_ddev,
wl_fixed_t x, wl_fixed_t y,
struct wl_data_offer *id)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
if (wl->dnd_offer != id) {
MP_FATAL(wl, "DND offer ID mismatch!\n");
return;
@@ -605,7 +770,8 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_ddev,
static void data_device_handle_leave(void *data, struct wl_data_device *wl_ddev)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
if (wl->dnd_offer) {
if (wl->dnd_fd != -1)
@@ -625,13 +791,15 @@ static void data_device_handle_leave(void *data, struct wl_data_device *wl_ddev)
static void data_device_handle_motion(void *data, struct wl_data_device *wl_ddev,
uint32_t time, wl_fixed_t x, wl_fixed_t y)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
wl_data_offer_accept(wl->dnd_offer, time, wl->dnd_mime_type);
}
static void data_device_handle_drop(void *data, struct wl_data_device *wl_ddev)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
int pipefd[2];
@@ -652,7 +820,8 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_ddev)
static void data_device_handle_selection(void *data, struct wl_data_device *wl_ddev,
struct wl_data_offer *id)
{
- struct vo_wayland_state *wl = data;
+ struct vo_wayland_seat *s = data;
+ struct vo_wayland_state *wl = s->wl;
if (wl->dnd_offer) {
wl_data_offer_destroy(wl->dnd_offer);
@@ -723,10 +892,8 @@ static void output_handle_done(void *data, struct wl_output *wl_output)
* geometry and scaling should be recalculated. */
if (wl->current_output && wl->current_output->output == wl_output) {
set_surface_scaling(wl);
- spawn_cursor(wl);
set_geometry(wl, false);
- prepare_resize(wl, 0, 0);
- wl->pending_vo_events |= VO_EVENT_DPI;
+ prepare_resize(wl);
}
wl->pending_vo_events |= VO_EVENT_WIN_STATE;
@@ -775,36 +942,23 @@ static void surface_handle_enter(void *data, struct wl_surface *wl_surface,
struct mp_rect old_geometry = wl->geometry;
wl->current_output = NULL;
+ int outputs = 0;
struct vo_wayland_output *o;
wl_list_for_each(o, &wl->output_list, link) {
if (o->output == output) {
wl->current_output = o;
- break;
+ wl->current_output->has_surface = true;
}
+ if (o->has_surface)
+ ++outputs;
}
- wl->current_output->has_surface = true;
- bool force_resize = false;
-
- if (!wl->fractional_scale_manager && wl_surface_get_version(wl_surface) < 6 &&
- wl->scaling != wl->current_output->scale)
- {
- set_surface_scaling(wl);
- spawn_cursor(wl);
- force_resize = true;
- wl->pending_vo_events |= VO_EVENT_DPI;
- }
-
- if (!mp_rect_equals(&old_output_geometry, &wl->current_output->geometry)) {
- set_geometry(wl, false);
- force_resize = true;
- }
-
- if (!mp_rect_equals(&old_geometry, &wl->geometry) || force_resize)
- prepare_resize(wl, 0, 0);
+ if (outputs == 1)
+ update_output_geometry(wl, old_geometry, old_output_geometry);
MP_VERBOSE(wl, "Surface entered output %s %s (0x%x), scale = %f, refresh rate = %f Hz\n",
- o->make, o->model, o->id, wl->scaling, o->refresh_rate);
+ wl->current_output->make, wl->current_output->model,
+ wl->current_output->id, wl->scaling, wl->current_output->refresh_rate);
wl->pending_vo_events |= VO_EVENT_WIN_STATE;
}
@@ -813,15 +967,27 @@ static void surface_handle_leave(void *data, struct wl_surface *wl_surface,
struct wl_output *output)
{
struct vo_wayland_state *wl = data;
+ if (!wl->current_output)
+ return;
+ struct mp_rect old_output_geometry = wl->current_output->geometry;
+ struct mp_rect old_geometry = wl->geometry;
+
+ int outputs = 0;
struct vo_wayland_output *o;
wl_list_for_each(o, &wl->output_list, link) {
- if (o->output == output) {
+ if (o->output == output)
o->has_surface = false;
- wl->pending_vo_events |= VO_EVENT_WIN_STATE;
- return;
- }
+ if (o->has_surface)
+ ++outputs;
+ if (o->output != output && o->has_surface)
+ wl->current_output = o;
}
+
+ if (outputs == 1)
+ update_output_geometry(wl, old_geometry, old_output_geometry);
+
+ wl->pending_vo_events |= VO_EVENT_WIN_STATE;
}
#ifdef HAVE_WAYLAND_1_22
@@ -830,21 +996,20 @@ static void surface_handle_preferred_buffer_scale(void *data,
int32_t scale)
{
struct vo_wayland_state *wl = data;
- double old_scale = wl->scaling;
- if (wl->fractional_scale_manager)
+ if (wl->fractional_scale_manager || wl->scaling == scale)
return;
- // dmabuf_wayland is always wl->scaling = 1
- wl->scaling = !wl->using_dmabuf_wayland ? scale : 1;
+ wl->pending_scaling = scale;
+ wl->scale_configured = true;
MP_VERBOSE(wl, "Obtained preferred scale, %f, from the compositor.\n",
wl->scaling);
wl->pending_vo_events |= VO_EVENT_DPI;
- if (wl->current_output) {
- rescale_geometry(wl, old_scale);
- set_geometry(wl, false);
- prepare_resize(wl, 0, 0);
- }
+ wl->need_rescale = true;
+
+ // Update scaling now.
+ if (single_output_spanned(wl))
+ update_output_scaling(wl);
}
static void surface_handle_preferred_buffer_transform(void *data,
@@ -889,12 +1054,12 @@ static void handle_toplevel_config(void *data, struct xdg_toplevel *toplevel,
struct mp_vo_opts *vo_opts = wl->vo_opts;
struct mp_rect old_geometry = wl->geometry;
- int old_toplevel_width = wl->toplevel_width;
- int old_toplevel_height = wl->toplevel_height;
- wl->toplevel_width = width;
- wl->toplevel_height = height;
+ if (width < 0 || height < 0) {
+ MP_WARN(wl, "Compositor sent negative width/height values. Treating them as zero.\n");
+ width = height = 0;
+ }
- if (!wl->configured) {
+ if (!wl->geometry_configured) {
/* Save initial window size if the compositor gives us a hint here. */
bool autofit_or_geometry = vo_opts->geometry.wh_valid || vo_opts->autofit.wh_valid ||
vo_opts->autofit_larger.wh_valid || vo_opts->autofit_smaller.wh_valid;
@@ -949,20 +1114,24 @@ static void handle_toplevel_config(void *data, struct xdg_toplevel *toplevel,
wl->hidden = is_suspended;
if (vo_opts->fullscreen != is_fullscreen) {
- wl->state_change = true;
+ wl->state_change = wl->reconfigured;
vo_opts->fullscreen = is_fullscreen;
m_config_cache_write_opt(wl->vo_opts_cache, &vo_opts->fullscreen);
}
if (vo_opts->window_maximized != is_maximized) {
- wl->state_change = true;
+ wl->state_change = wl->reconfigured;
vo_opts->window_maximized = is_maximized;
m_config_cache_write_opt(wl->vo_opts_cache, &vo_opts->window_maximized);
}
+ if (!is_tiled && wl->tiled)
+ wl->state_change = wl->reconfigured;
+
wl->tiled = is_tiled;
wl->locked_size = is_fullscreen || is_maximized || is_tiled;
+ wl->reconfigured = false;
if (wl->requested_decoration)
request_decoration_mode(wl, wl->requested_decoration);
@@ -993,26 +1162,17 @@ static void handle_toplevel_config(void *data, struct xdg_toplevel *toplevel,
goto resize;
}
- if (old_toplevel_width == wl->toplevel_width &&
- old_toplevel_height == wl->toplevel_height)
- return;
-
if (!wl->locked_size) {
- if (vo_opts->keepaspect) {
- double scale_factor = (double)width / wl->reduced_width;
- width = ceil(wl->reduced_width * scale_factor);
- if (vo_opts->keepaspect_window)
- height = ceil(wl->reduced_height * scale_factor);
- }
+ apply_keepaspect(wl, &width, &height);
wl->window_size.x0 = 0;
wl->window_size.y0 = 0;
- wl->window_size.x1 = round(width * wl->scaling);
- wl->window_size.y1 = round(height * wl->scaling);
+ wl->window_size.x1 = lround(width * wl->scaling);
+ wl->window_size.y1 = lround(height * wl->scaling);
}
wl->geometry.x0 = 0;
wl->geometry.y0 = 0;
- wl->geometry.x1 = round(width * wl->scaling);
- wl->geometry.y1 = round(height * wl->scaling);
+ wl->geometry.x1 = lround(width * wl->scaling);
+ wl->geometry.y1 = lround(height * wl->scaling);
if (mp_rect_equals(&old_geometry, &wl->geometry))
return;
@@ -1022,7 +1182,7 @@ resize:
mp_rect_w(old_geometry), mp_rect_h(old_geometry),
mp_rect_w(wl->geometry), mp_rect_h(wl->geometry));
- prepare_resize(wl, width, height);
+ prepare_resize(wl);
wl->toplevel_configured = true;
}
@@ -1062,18 +1222,19 @@ static void preferred_scale(void *data,
uint32_t scale)
{
struct vo_wayland_state *wl = data;
- double old_scale = wl->scaling;
+ double new_scale = (double)scale / 120;
+ if (wl->scaling == new_scale)
+ return;
- // dmabuf_wayland is always wl->scaling = 1
- wl->scaling = !wl->using_dmabuf_wayland ? (double)scale / 120 : 1;
+ wl->pending_scaling = new_scale;
+ wl->scale_configured = true;
MP_VERBOSE(wl, "Obtained preferred scale, %f, from the compositor.\n",
- wl->scaling);
- wl->pending_vo_events |= VO_EVENT_DPI;
- if (wl->current_output) {
- rescale_geometry(wl, old_scale);
- set_geometry(wl, false);
- prepare_resize(wl, 0, 0);
- }
+ wl->pending_scaling);
+ wl->need_rescale = true;
+
+ // Update scaling now.
+ if (single_output_spanned(wl))
+ update_output_scaling(wl);
}
static const struct wp_fractional_scale_v1_listener fractional_scale_listener = {
@@ -1296,9 +1457,8 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id
wl_surface_add_listener(wl->surface, &surface_listener, wl);
}
- if (!strcmp(interface, wl_subcompositor_interface.name) && (ver >= 1) && found++) {
+ if (!strcmp(interface, wl_subcompositor_interface.name) && (ver >= 1) && found++)
wl->subcompositor = wl_registry_bind(reg, id, &wl_subcompositor_interface, 1);
- }
if (!strcmp (interface, zwp_linux_dmabuf_v1_interface.name) && (ver >= 4) && found++) {
wl->dmabuf = wl_registry_bind(reg, id, &zwp_linux_dmabuf_v1_interface, 4);
@@ -1306,13 +1466,11 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id
zwp_linux_dmabuf_feedback_v1_add_listener(wl->dmabuf_feedback, &dmabuf_feedback_listener, wl);
}
- if (!strcmp (interface, wp_viewporter_interface.name) && (ver >= 1) && found++) {
- wl->viewporter = wl_registry_bind (reg, id, &wp_viewporter_interface, 1);
- }
+ if (!strcmp (interface, wp_viewporter_interface.name) && (ver >= 1) && found++)
+ wl->viewporter = wl_registry_bind (reg, id, &wp_viewporter_interface, 1);
- if (!strcmp(interface, wl_data_device_manager_interface.name) && (ver >= 3) && found++) {
+ if (!strcmp(interface, wl_data_device_manager_interface.name) && (ver >= 3) && found++)
wl->dnd_devman = wl_registry_bind(reg, id, &wl_data_device_manager_interface, 3);
- }
if (!strcmp(interface, wl_output_interface.name) && (ver >= 2) && found++) {
struct vo_wayland_output *output = talloc_zero(wl, struct vo_wayland_output);
@@ -1329,34 +1487,41 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id
}
if (!strcmp(interface, wl_seat_interface.name) && found++) {
- wl->seat = wl_registry_bind(reg, id, &wl_seat_interface, 1);
- wl_seat_add_listener(wl->seat, &seat_listener, wl);
+ if (ver < 5)
+ MP_WARN(wl, "Scrolling won't work because the compositor doesn't "
+ "support version 5 of wl_seat protocol!\n");
+#ifdef HAVE_WAYLAND_1_21
+ ver = MPMIN(ver, 8); /* Cap at 8 in case new events are added later. */
+#else
+ ver = MPMIN(ver, 7);
+#endif
+ struct vo_wayland_seat *seat = talloc_zero(wl, struct vo_wayland_seat);
+ seat->wl = wl;
+ seat->id = id;
+ seat->seat = wl_registry_bind(reg, id, &wl_seat_interface, ver);
+ wl_seat_add_listener(seat->seat, &seat_listener, seat);
+ wl_list_insert(&wl->seat_list, &seat->link);
}
- if (!strcmp(interface, wl_shm_interface.name) && found++) {
+ if (!strcmp(interface, wl_shm_interface.name) && found++)
wl->shm = wl_registry_bind(reg, id, &wl_shm_interface, 1);
- }
#if HAVE_WAYLAND_PROTOCOLS_1_27
- if (!strcmp(interface, wp_content_type_manager_v1_interface.name) && found++) {
+ if (!strcmp(interface, wp_content_type_manager_v1_interface.name) && found++)
wl->content_type_manager = wl_registry_bind(reg, id, &wp_content_type_manager_v1_interface, 1);
- }
- if (!strcmp(interface, wp_single_pixel_buffer_manager_v1_interface.name) && found++) {
+ if (!strcmp(interface, wp_single_pixel_buffer_manager_v1_interface.name) && found++)
wl->single_pixel_manager = wl_registry_bind(reg, id, &wp_single_pixel_buffer_manager_v1_interface, 1);
- }
#endif
#if HAVE_WAYLAND_PROTOCOLS_1_31
- if (!strcmp(interface, wp_fractional_scale_manager_v1_interface.name) && found++) {
+ if (!strcmp(interface, wp_fractional_scale_manager_v1_interface.name) && found++)
wl->fractional_scale_manager = wl_registry_bind(reg, id, &wp_fractional_scale_manager_v1_interface, 1);
- }
#endif
#if HAVE_WAYLAND_PROTOCOLS_1_32
- if (!strcmp(interface, wp_cursor_shape_manager_v1_interface.name) && found++) {
+ if (!strcmp(interface, wp_cursor_shape_manager_v1_interface.name) && found++)
wl->cursor_shape_manager = wl_registry_bind(reg, id, &wp_cursor_shape_manager_v1_interface, 1);
- }
#endif
if (!strcmp(interface, wp_presentation_interface.name) && found++) {
@@ -1370,13 +1535,11 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id
xdg_wm_base_add_listener(wl->wm_base, &xdg_wm_base_listener, wl);
}
- if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name) && found++) {
+ if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name) && found++)
wl->xdg_decoration_manager = wl_registry_bind(reg, id, &zxdg_decoration_manager_v1_interface, 1);
- }
- if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) && found++) {
+ if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) && found++)
wl->idle_inhibit_manager = wl_registry_bind(reg, id, &zwp_idle_inhibit_manager_v1_interface, 1);
- }
if (found > 1)
MP_VERBOSE(wl, "Registered for protocol %s\n", interface);
@@ -1385,13 +1548,21 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id
static void registry_handle_remove(void *data, struct wl_registry *reg, uint32_t id)
{
struct vo_wayland_state *wl = data;
- struct vo_wayland_output *output, *tmp;
- wl_list_for_each_safe(output, tmp, &wl->output_list, link) {
+ struct vo_wayland_output *output, *output_tmp;
+ wl_list_for_each_safe(output, output_tmp, &wl->output_list, link) {
if (output->id == id) {
remove_output(output);
return;
}
}
+
+ struct vo_wayland_seat *seat, *seat_tmp;
+ wl_list_for_each_safe(seat, seat_tmp, &wl->seat_list, link) {
+ if (seat->id == id) {
+ remove_seat(seat);
+ return;
+ }
+ }
}
static const struct wl_registry_listener registry_listener = {
@@ -1400,6 +1571,26 @@ static const struct wl_registry_listener registry_listener = {
};
/* Static functions */
+static void apply_keepaspect(struct vo_wayland_state *wl, int *width, int *height)
+{
+ if (!wl->vo_opts->keepaspect)
+ return;
+
+ double scale_factor = (double)*width / wl->reduced_width;
+ *width = ceil(wl->reduced_width * scale_factor);
+ if (wl->vo_opts->keepaspect_window)
+ *height = ceil(wl->reduced_height * scale_factor);
+}
+
+static void free_dnd_data(struct vo_wayland_state *wl)
+{
+ // caller should close wl->dnd_fd if appropriate
+
+ wl->dnd_action = -1;
+ TA_FREEP(&wl->dnd_mime_type);
+ wl->dnd_mime_score = 0;
+}
+
static void check_dnd_fd(struct vo_wayland_state *wl)
{
if (wl->dnd_fd == -1)
@@ -1410,40 +1601,44 @@ static void check_dnd_fd(struct vo_wayland_state *wl)
return;
if (fdp.revents & POLLIN) {
- ptrdiff_t offset = 0;
- size_t data_read = 0;
- const size_t chunk_size = 1;
- uint8_t *buffer = ta_zalloc_size(wl, chunk_size);
- if (!buffer)
- goto end;
-
- while ((data_read = read(wl->dnd_fd, buffer + offset, chunk_size)) > 0) {
- offset += data_read;
- buffer = ta_realloc_size(wl, buffer, offset + chunk_size);
- memset(buffer + offset, 0, chunk_size);
- if (!buffer)
- goto end;
+ ssize_t data_read = 0;
+ const size_t chunk_size = 256;
+ bstr file_list = {
+ .start = talloc_zero_size(NULL, chunk_size),
+ };
+
+ while (1) {
+ data_read = read(wl->dnd_fd, file_list.start + file_list.len, chunk_size);
+ if (data_read == -1 && errno == EINTR)
+ continue;
+ else if (data_read <= 0)
+ break;
+ file_list.len += data_read;
+ file_list.start = talloc_realloc_size(NULL, file_list.start, file_list.len + chunk_size);
+ memset(file_list.start + file_list.len, 0, chunk_size);
}
- MP_VERBOSE(wl, "Read %td bytes from the DND fd\n", offset);
+ if (data_read == -1) {
+ MP_VERBOSE(wl, "DND aborted (read error)\n");
+ } else {
+ MP_VERBOSE(wl, "Read %zu bytes from the DND fd\n", file_list.len);
- struct bstr file_list = bstr0(buffer);
- mp_event_drop_mime_data(wl->vo->input_ctx, wl->dnd_mime_type,
- file_list, wl->dnd_action);
- talloc_free(buffer);
-end:
- if (wl->dnd_mime_type)
- talloc_free(wl->dnd_mime_type);
+ if (wl->dnd_offer)
+ wl_data_offer_finish(wl->dnd_offer);
- if (wl->dnd_action >= 0 && wl->dnd_offer)
- wl_data_offer_finish(wl->dnd_offer);
+ assert(wl->dnd_action >= 0);
+ mp_event_drop_mime_data(wl->vo->input_ctx, wl->dnd_mime_type,
+ file_list, wl->dnd_action);
+ }
- wl->dnd_action = -1;
- wl->dnd_mime_type = NULL;
- wl->dnd_mime_score = 0;
+ talloc_free(file_list.start);
+ free_dnd_data(wl);
}
if (fdp.revents & (POLLIN | POLLERR | POLLHUP)) {
+ if (wl->dnd_action >= 0)
+ MP_VERBOSE(wl, "DND aborted (hang up or error)\n");
+ free_dnd_data(wl);
close(wl->dnd_fd);
wl->dnd_fd = -1;
}
@@ -1499,13 +1694,12 @@ static bool create_input(struct vo_wayland_state *wl)
static int create_viewports(struct vo_wayland_state *wl)
{
- if (wl->viewporter) {
- wl->viewport = wp_viewporter_get_viewport(wl->viewporter, wl->surface);
- wl->osd_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->osd_surface);
- wl->video_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->video_surface);
- }
+ wl->viewport = wp_viewporter_get_viewport(wl->viewporter, wl->surface);
+ wl->cursor_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->cursor_surface);
+ wl->osd_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->osd_surface);
+ wl->video_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->video_surface);
- if (wl->viewporter && (!wl->viewport || !wl->osd_viewport || !wl->video_viewport)) {
+ if (!wl->viewport || !wl->osd_viewport || !wl->video_viewport) {
MP_ERR(wl, "failed to create viewport interfaces!\n");
return 1;
}
@@ -1544,8 +1738,6 @@ static void add_feedback(struct vo_wayland_feedback_pool *fback_pool,
static void do_minimize(struct vo_wayland_state *wl)
{
- if (!wl->xdg_toplevel)
- return;
if (wl->vo_opts->window_minimized)
xdg_toplevel_set_minimized(wl->xdg_toplevel);
}
@@ -1566,7 +1758,7 @@ static char **get_displays_spanned(struct vo_wayland_state *wl)
return names;
}
-static int get_mods(struct vo_wayland_state *wl)
+static int get_mods(struct vo_wayland_seat *s)
{
static char* const mod_names[] = {
XKB_MOD_NAME_SHIFT,
@@ -1585,21 +1777,21 @@ static int get_mods(struct vo_wayland_state *wl)
int modifiers = 0;
for (int n = 0; n < MP_ARRAY_SIZE(mods); n++) {
- xkb_mod_index_t index = xkb_keymap_mod_get_index(wl->xkb_keymap, mod_names[n]);
- if (!xkb_state_mod_index_is_consumed(wl->xkb_state, wl->keyboard_code, index)
- && xkb_state_mod_index_is_active(wl->xkb_state, index,
- XKB_STATE_MODS_DEPRESSED))
+ xkb_mod_index_t index = xkb_keymap_mod_get_index(s->xkb_keymap, mod_names[n]);
+ if (index != XKB_MOD_INVALID
+ && xkb_state_mod_index_is_active(s->xkb_state, index,
+ XKB_STATE_MODS_EFFECTIVE))
modifiers |= mods[n];
}
return modifiers;
}
-static void get_shape_device(struct vo_wayland_state *wl)
+static void get_shape_device(struct vo_wayland_state *wl, struct vo_wayland_seat *s)
{
#if HAVE_WAYLAND_PROTOCOLS_1_32
- if (!wl->cursor_shape_device && wl->cursor_shape_manager) {
- wl->cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(wl->cursor_shape_manager,
- wl->pointer);
+ if (!s->cursor_shape_device && wl->cursor_shape_manager) {
+ s->cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(wl->cursor_shape_manager,
+ s->pointer);
}
#endif
}
@@ -1616,8 +1808,17 @@ static void guess_focus(struct vo_wayland_state *wl)
{
// We can't actually know if the window is focused or not in wayland,
// so just guess it with some common sense. Obviously won't work if
- // the user has no keyboard.
- if ((!wl->focused && wl->activated && wl->has_keyboard_input) ||
+ // the user has no keyboard. We flag has_keyboard_input if
+ // at least one seat has it.
+ bool has_keyboard_input = false;
+ struct vo_wayland_seat *seat;
+ wl_list_for_each(seat, &wl->seat_list, link) {
+ if (seat->has_keyboard_input) {
+ has_keyboard_input = true;
+ }
+ }
+
+ if ((!wl->focused && wl->activated && has_keyboard_input) ||
(wl->focused && !wl->activated))
{
wl->focused = !wl->focused;
@@ -1672,12 +1873,10 @@ static int lookupkey(int key)
return mpkey;
}
-static void prepare_resize(struct vo_wayland_state *wl, int width, int height)
+static void prepare_resize(struct vo_wayland_state *wl)
{
- if (!width)
- width = mp_rect_w(wl->geometry) / wl->scaling;
- if (!height)
- height = mp_rect_h(wl->geometry) / wl->scaling;
+ int32_t width = mp_rect_w(wl->geometry) / wl->scaling;
+ int32_t height = mp_rect_h(wl->geometry) / wl->scaling;
xdg_surface_set_window_geometry(wl->xdg_surface, 0, 0, width, height);
wl->pending_vo_events |= VO_EVENT_RESIZE;
}
@@ -1690,6 +1889,9 @@ static void request_decoration_mode(struct vo_wayland_state *wl, uint32_t mode)
static void rescale_geometry(struct vo_wayland_state *wl, double old_scale)
{
+ if (!wl->vo_opts->hidpi_window_scale && !wl->locked_size)
+ return;
+
double factor = old_scale / wl->scaling;
wl->window_size.x1 /= factor;
wl->window_size.y1 /= factor;
@@ -1734,6 +1936,37 @@ static void remove_output(struct vo_wayland_output *out)
return;
}
+static void remove_seat(struct vo_wayland_seat *seat)
+{
+ if (!seat)
+ return;
+
+ MP_VERBOSE(seat->wl, "Deregistering seat 0x%x\n", seat->id);
+ wl_list_remove(&seat->link);
+ if (seat == seat->wl->last_button_seat)
+ seat->wl->last_button_seat = NULL;
+ if (seat->keyboard)
+ wl_keyboard_destroy(seat->keyboard);
+ if (seat->pointer)
+ wl_pointer_destroy(seat->pointer);
+ if (seat->touch)
+ wl_touch_destroy(seat->touch);
+ if (seat->dnd_ddev)
+ wl_data_device_destroy(seat->dnd_ddev);
+#if HAVE_WAYLAND_PROTOCOLS_1_32
+ if (seat->cursor_shape_device)
+ wp_cursor_shape_device_v1_destroy(seat->cursor_shape_device);
+#endif
+ if (seat->xkb_keymap)
+ xkb_keymap_unref(seat->xkb_keymap);
+ if (seat->xkb_state)
+ xkb_state_unref(seat->xkb_state);
+
+ wl_seat_destroy(seat->seat);
+ talloc_free(seat);
+ return;
+}
+
static void set_content_type(struct vo_wayland_state *wl)
{
if (!wl->content_type_manager)
@@ -1748,20 +1981,23 @@ static void set_content_type(struct vo_wayland_state *wl)
#endif
}
-static void set_cursor_shape(struct vo_wayland_state *wl)
+static void set_cursor_shape(struct vo_wayland_seat *s)
{
#if HAVE_WAYLAND_PROTOCOLS_1_32
- wp_cursor_shape_device_v1_set_shape(wl->cursor_shape_device, wl->pointer_id,
+ wp_cursor_shape_device_v1_set_shape(s->cursor_shape_device, s->pointer_enter_serial,
WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
#endif
}
-static int set_cursor_visibility(struct vo_wayland_state *wl, bool on)
+static int set_cursor_visibility(struct vo_wayland_seat *s, bool on)
{
+ if (!s)
+ return VO_FALSE;
+ struct vo_wayland_state *wl = s->wl;
wl->cursor_visible = on;
if (on) {
- if (wl->cursor_shape_device) {
- set_cursor_shape(wl);
+ if (s->cursor_shape_device) {
+ set_cursor_shape(s);
} else {
if (spawn_cursor(wl))
return VO_FALSE;
@@ -1770,19 +2006,41 @@ static int set_cursor_visibility(struct vo_wayland_state *wl, bool on)
if (!buffer)
return VO_FALSE;
int scale = MPMAX(wl->scaling, 1);
- wl_pointer_set_cursor(wl->pointer, wl->pointer_id, wl->cursor_surface,
+ wl_pointer_set_cursor(s->pointer, s->pointer_enter_serial, wl->cursor_surface,
img->hotspot_x / scale, img->hotspot_y / scale);
- wl_surface_set_buffer_scale(wl->cursor_surface, scale);
+ wp_viewport_set_destination(wl->cursor_viewport, lround(img->width / scale),
+ lround(img->height / scale));
wl_surface_attach(wl->cursor_surface, buffer, 0, 0);
wl_surface_damage_buffer(wl->cursor_surface, 0, 0, img->width, img->height);
}
wl_surface_commit(wl->cursor_surface);
} else {
- wl_pointer_set_cursor(wl->pointer, wl->pointer_id, NULL, 0, 0);
+ wl_pointer_set_cursor(s->pointer, s->pointer_enter_serial, NULL, 0, 0);
}
return VO_TRUE;
}
+static int set_cursor_visibility_all_seats(struct vo_wayland_state *wl, bool on)
+{
+ bool unavailable = true;
+ bool failed = false;
+ struct vo_wayland_seat *seat;
+ wl_list_for_each(seat, &wl->seat_list, link) {
+ if (seat->pointer) {
+ unavailable = false;
+ if (set_cursor_visibility(seat, on) == VO_FALSE)
+ failed = true;
+ }
+ }
+
+ if (unavailable)
+ return VO_NOTAVAIL;
+ if (failed)
+ return VO_FALSE;
+
+ return VO_TRUE;
+}
+
static void set_geometry(struct vo_wayland_state *wl, bool resize)
{
struct vo *vo = wl->vo;
@@ -1805,7 +2063,7 @@ static void set_geometry(struct vo_wayland_state *wl, bool resize)
if (resize) {
if (!wl->locked_size)
wl->geometry = wl->window_size;
- prepare_resize(wl, 0, 0);
+ prepare_resize(wl);
}
}
@@ -1840,15 +2098,16 @@ static int set_screensaver_inhibitor(struct vo_wayland_state *wl, int state)
static void set_surface_scaling(struct vo_wayland_state *wl)
{
- if (wl->fractional_scale_manager)
+ if (wl->scale_configured && (wl->fractional_scale_manager ||
+ wl_surface_get_version(wl->surface) >= 6))
+ {
return;
+ }
- // dmabuf_wayland is always wl->scaling = 1
double old_scale = wl->scaling;
- wl->scaling = !wl->using_dmabuf_wayland ? wl->current_output->scale : 1;
-
+ wl->scaling = wl->current_output->scale;
rescale_geometry(wl, old_scale);
- wl_surface_set_buffer_scale(wl->surface, wl->scaling);
+ wl->pending_vo_events |= VO_EVENT_DPI;
}
static void set_window_bounds(struct vo_wayland_state *wl)
@@ -1862,22 +2121,34 @@ static void set_window_bounds(struct vo_wayland_state *wl)
return;
}
+ apply_keepaspect(wl, &wl->bounded_width, &wl->bounded_height);
+
if (wl->bounded_width && wl->bounded_width < wl->window_size.x1)
wl->window_size.x1 = wl->bounded_width;
if (wl->bounded_height && wl->bounded_height < wl->window_size.y1)
wl->window_size.y1 = wl->bounded_height;
}
+static bool single_output_spanned(struct vo_wayland_state *wl)
+{
+ int outputs = 0;
+ struct vo_wayland_output *output;
+ wl_list_for_each(output, &wl->output_list, link) {
+ if (output->has_surface)
+ ++outputs;
+ if (outputs > 1)
+ return false;
+ }
+ return wl->current_output && outputs == 1;
+}
+
static int spawn_cursor(struct vo_wayland_state *wl)
{
- /* Don't use this if we have cursor-shape. */
- if (wl->cursor_shape_device)
- return 0;
- /* Reuse if size is identical */
- if (!wl->pointer || wl->allocated_cursor_scale == wl->scaling)
+ if (wl->allocated_cursor_scale == wl->scaling) {
return 0;
- else if (wl->cursor_theme)
+ } else if (wl->cursor_theme) {
wl_cursor_theme_destroy(wl->cursor_theme);
+ }
const char *xcursor_theme = getenv("XCURSOR_THEME");
const char *size_str = getenv("XCURSOR_SIZE");
@@ -1896,9 +2167,12 @@ static int spawn_cursor(struct vo_wayland_state *wl)
return 1;
}
- wl->default_cursor = wl_cursor_theme_get_cursor(wl->cursor_theme, "left_ptr");
+ wl->default_cursor = wl_cursor_theme_get_cursor(wl->cursor_theme, "default");
+ if (!wl->default_cursor)
+ wl->default_cursor = wl_cursor_theme_get_cursor(wl->cursor_theme, "left_ptr");
+
if (!wl->default_cursor) {
- MP_ERR(wl, "Unable to load cursor theme!\n");
+ MP_ERR(wl, "Unable to get default and left_ptr XCursor from theme!\n");
return 1;
}
@@ -1909,9 +2183,6 @@ static int spawn_cursor(struct vo_wayland_state *wl)
static void toggle_fullscreen(struct vo_wayland_state *wl)
{
- if (!wl->xdg_toplevel)
- return;
- wl->state_change = true;
bool specific_screen = wl->vo_opts->fsscreen_id >= 0 || wl->vo_opts->fsscreen_name;
if (wl->vo_opts->fullscreen && !specific_screen) {
xdg_toplevel_set_fullscreen(wl->xdg_toplevel, NULL);
@@ -1919,33 +2190,66 @@ static void toggle_fullscreen(struct vo_wayland_state *wl)
struct vo_wayland_output *output = find_output(wl);
xdg_toplevel_set_fullscreen(wl->xdg_toplevel, output->output);
} else {
+ wl->state_change = wl->reconfigured;
xdg_toplevel_unset_fullscreen(wl->xdg_toplevel);
}
}
static void toggle_maximized(struct vo_wayland_state *wl)
{
- if (!wl->xdg_toplevel)
- return;
- wl->state_change = true;
if (wl->vo_opts->window_maximized) {
xdg_toplevel_set_maximized(wl->xdg_toplevel);
} else {
+ wl->state_change = wl->reconfigured;
xdg_toplevel_unset_maximized(wl->xdg_toplevel);
}
}
static void update_app_id(struct vo_wayland_state *wl)
{
- if (!wl->xdg_toplevel)
- return;
xdg_toplevel_set_app_id(wl->xdg_toplevel, wl->vo_opts->appid);
}
+static void update_output_scaling(struct vo_wayland_state *wl)
+{
+ double old_scale = wl->scaling;
+ wl->scaling = wl->pending_scaling;
+ rescale_geometry(wl, old_scale);
+ set_geometry(wl, false);
+ prepare_resize(wl);
+ wl->need_rescale = false;
+ wl->pending_vo_events |= VO_EVENT_DPI;
+}
+
+static void update_output_geometry(struct vo_wayland_state *wl, struct mp_rect old_geometry,
+ struct mp_rect old_output_geometry)
+{
+ if (wl->need_rescale) {
+ update_output_scaling(wl);
+ return;
+ }
+
+ bool force_resize = false;
+ bool use_output_scale = wl_surface_get_version(wl->surface) < 6 &&
+ !wl->fractional_scale_manager &&
+ wl->scaling != wl->current_output->scale;
+
+ if (use_output_scale) {
+ set_surface_scaling(wl);
+ force_resize = true;
+ }
+
+ if (!mp_rect_equals(&old_output_geometry, &wl->current_output->geometry)) {
+ set_geometry(wl, false);
+ force_resize = true;
+ }
+
+ if (!mp_rect_equals(&old_geometry, &wl->geometry) || force_resize)
+ prepare_resize(wl);
+}
+
static int update_window_title(struct vo_wayland_state *wl, const char *title)
{
- if (!wl->xdg_toplevel)
- return VO_NOTAVAIL;
/* The xdg-shell protocol requires that the title is UTF-8. */
void *tmp = talloc_new(NULL);
struct bstr b_title = bstr_sanitize_utf8_latin1(tmp, bstr0(title));
@@ -1954,12 +2258,6 @@ static int update_window_title(struct vo_wayland_state *wl, const char *title)
return VO_TRUE;
}
-static void window_move(struct vo_wayland_state *wl, uint32_t serial)
-{
- if (wl->xdg_toplevel)
- xdg_toplevel_move(wl->xdg_toplevel, wl->seat, serial);
-}
-
static void wayland_dispatch_events(struct vo_wayland_state *wl, int nfds, int64_t timeout_ns)
{
if (wl->display_fd == -1)
@@ -1994,6 +2292,18 @@ static void wayland_dispatch_events(struct vo_wayland_state *wl, int nfds, int64
wl_display_dispatch_pending(wl->display);
}
+static void begin_dragging(struct vo_wayland_state *wl)
+{
+ struct vo_wayland_seat *s = wl->last_button_seat;
+ if (!mp_input_test_dragging(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y) &&
+ !wl->locked_size && s)
+ {
+ xdg_toplevel_move(wl->xdg_toplevel, s->seat, s->pointer_button_serial);
+ wl->last_button_seat = NULL;
+ mp_input_put_key(wl->vo->input_ctx, MP_INPUT_RELEASE_ALL);
+ }
+}
+
/* Non-static */
int vo_wayland_allocate_memfd(struct vo *vo, size_t size)
{
@@ -2076,8 +2386,6 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
set_input_region(wl, opts->cursor_passthrough);
if (opt == &opts->fullscreen)
toggle_fullscreen(wl);
- if (opt == &opts->hidpi_window_scale)
- set_geometry(wl, true);
if (opt == &opts->window_maximized)
toggle_maximized(wl);
if (opt == &opts->window_minimized)
@@ -2085,6 +2393,7 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
if (opt == &opts->geometry || opt == &opts->autofit ||
opt == &opts->autofit_smaller || opt == &opts->autofit_larger)
{
+ wl->state_change = true;
set_geometry(wl, true);
}
}
@@ -2123,6 +2432,7 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
wl->window_size.x1 = s[0];
wl->window_size.y1 = s[1];
if (!wl->vo_opts->fullscreen && !wl->tiled) {
+ wl->state_change = true;
if (wl->vo_opts->window_maximized) {
xdg_toplevel_unset_maximized(wl->xdg_toplevel);
wl_display_dispatch_pending(wl->display);
@@ -2131,7 +2441,7 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
return VO_TRUE;
}
wl->geometry = wl->window_size;
- prepare_resize(wl, 0, 0);
+ prepare_resize(wl);
}
return VO_TRUE;
}
@@ -2166,12 +2476,13 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
*(double *)arg = wl->scaling;
return VO_TRUE;
}
+ case VOCTRL_BEGIN_DRAGGING:
+ begin_dragging(wl);
+ return VO_TRUE;
case VOCTRL_UPDATE_WINDOW_TITLE:
return update_window_title(wl, (const char *)arg);
case VOCTRL_SET_CURSOR_VISIBILITY:
- if (!wl->pointer)
- return VO_NOTAVAIL;
- return set_cursor_visibility(wl, *(bool *)arg);
+ return set_cursor_visibility_all_seats(wl, *(bool *)arg);
case VOCTRL_KILL_SCREENSAVER:
return set_screensaver_inhibitor(wl, true);
case VOCTRL_RESTORE_SCREENSAVER:
@@ -2181,16 +2492,18 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
return VO_NOTIMPL;
}
-void vo_wayland_handle_fractional_scale(struct vo_wayland_state *wl)
+void vo_wayland_handle_scale(struct vo_wayland_state *wl)
{
- if (wl->fractional_scale_manager && wl->viewport)
- wp_viewport_set_destination(wl->viewport,
- round(mp_rect_w(wl->geometry) / wl->scaling),
- round(mp_rect_h(wl->geometry) / wl->scaling));
+ wp_viewport_set_destination(wl->viewport,
+ lround(mp_rect_w(wl->geometry) / wl->scaling),
+ lround(mp_rect_h(wl->geometry) / wl->scaling));
}
bool vo_wayland_init(struct vo *vo)
{
+ if (!getenv("WAYLAND_DISPLAY"))
+ goto err;
+
vo->wl = talloc_zero(NULL, struct vo_wayland_state);
struct vo_wayland_state *wl = vo->wl;
@@ -2209,9 +2522,10 @@ bool vo_wayland_init(struct vo *vo)
.vo_opts_cache = m_config_cache_alloc(wl, vo->global, &vo_sub_opts),
};
wl->vo_opts = wl->vo_opts_cache->opts;
- wl->using_dmabuf_wayland = !strcmp(wl->vo->driver->name, "dmabuf-wayland");
+ bool using_dmabuf_wayland = !strcmp(wl->vo->driver->name, "dmabuf-wayland");
wl_list_init(&wl->output_list);
+ wl_list_init(&wl->seat_list);
if (!wl->display)
goto err;
@@ -2243,6 +2557,12 @@ bool vo_wayland_init(struct vo *vo)
goto err;
}
+ if (!wl->viewporter) {
+ MP_FATAL(wl, "Compositor doesn't support the required %s protocol!\n",
+ wp_viewporter_interface.name);
+ goto err;
+ }
+
/* Can't be initialized during registry due to multi-protocol dependence */
if (create_viewports(wl))
goto err;
@@ -2279,10 +2599,13 @@ bool vo_wayland_init(struct vo *vo)
}
#endif
- if (wl->dnd_devman && wl->seat) {
- wl->dnd_ddev = wl_data_device_manager_get_data_device(wl->dnd_devman, wl->seat);
- wl_data_device_add_listener(wl->dnd_ddev, &data_device_listener, wl);
- } else if (!wl->dnd_devman) {
+ if (wl->dnd_devman) {
+ struct vo_wayland_seat *seat;
+ wl_list_for_each(seat, &wl->seat_list, link) {
+ seat->dnd_ddev = wl_data_device_manager_get_data_device(wl->dnd_devman, seat->seat);
+ wl_data_device_add_listener(seat->dnd_ddev, &data_device_listener, seat);
+ }
+ } else {
MP_VERBOSE(wl, "Compositor doesn't support the %s (ver. 3) protocol!\n",
wl_data_device_manager_interface.name);
}
@@ -2325,7 +2648,7 @@ bool vo_wayland_init(struct vo *vo)
update_app_id(wl);
mp_make_wakeup_pipe(wl->wakeup_pipe);
- wl->callback_surface = wl->using_dmabuf_wayland ? wl->video_surface : wl->surface;
+ wl->callback_surface = using_dmabuf_wayland ? wl->video_surface : wl->surface;
wl->frame_callback = wl_surface_frame(wl->callback_surface);
wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
wl_surface_commit(wl->surface);
@@ -2352,33 +2675,38 @@ bool vo_wayland_reconfig(struct vo *vo)
if (!wl->current_output)
return false;
set_surface_scaling(wl);
+ wl->scale_configured = true;
wl->pending_vo_events |= VO_EVENT_DPI;
}
- if (wl->vo_opts->auto_window_resize || !wl->configured)
+ if (wl->vo_opts->auto_window_resize || !wl->geometry_configured)
set_geometry(wl, false);
+ if (wl->geometry_configured && wl->vo_opts->auto_window_resize)
+ wl->reconfigured = true;
+
if (wl->opts->configure_bounds)
set_window_bounds(wl);
- if (!wl->configured || !wl->locked_size) {
- wl->geometry = wl->window_size;
- wl->configured = true;
- }
-
if (wl->vo_opts->cursor_passthrough)
set_input_region(wl, true);
- if (wl->vo_opts->fullscreen)
- toggle_fullscreen(wl);
+ if (!wl->geometry_configured || !wl->locked_size)
+ wl->geometry = wl->window_size;
- if (wl->vo_opts->window_maximized)
- toggle_maximized(wl);
+ if (!wl->geometry_configured) {
+ if (wl->vo_opts->fullscreen)
+ toggle_fullscreen(wl);
- if (wl->vo_opts->window_minimized)
- do_minimize(wl);
+ if (wl->vo_opts->window_maximized)
+ toggle_maximized(wl);
- prepare_resize(wl, 0, 0);
+ if (wl->vo_opts->window_minimized)
+ do_minimize(wl);
+ wl->geometry_configured = true;
+ }
+
+ prepare_resize(wl);
return true;
}
@@ -2403,6 +2731,10 @@ void vo_wayland_uninit(struct vo *vo)
if (!wl)
return;
+ if (wl->dnd_fd != -1)
+ close(wl->dnd_fd);
+ free_dnd_data(wl);
+
mp_input_put_key(wl->vo->input_ctx, MP_INPUT_RELEASE_ALL);
if (wl->compositor)
@@ -2412,9 +2744,6 @@ void vo_wayland_uninit(struct vo *vo)
wl_subcompositor_destroy(wl->subcompositor);
#if HAVE_WAYLAND_PROTOCOLS_1_32
- if (wl->cursor_shape_device)
- wp_cursor_shape_device_v1_destroy(wl->cursor_shape_device);
-
if (wl->cursor_shape_manager)
wp_cursor_shape_manager_v1_destroy(wl->cursor_shape_manager);
#endif
@@ -2433,9 +2762,6 @@ void vo_wayland_uninit(struct vo *vo)
wp_content_type_manager_v1_destroy(wl->content_type_manager);
#endif
- if (wl->dnd_ddev)
- wl_data_device_destroy(wl->dnd_ddev);
-
if (wl->dnd_devman)
wl_data_device_manager_destroy(wl->dnd_devman);
@@ -2462,12 +2788,6 @@ void vo_wayland_uninit(struct vo *vo)
if (wl->idle_inhibit_manager)
zwp_idle_inhibit_manager_v1_destroy(wl->idle_inhibit_manager);
- if (wl->keyboard)
- wl_keyboard_destroy(wl->keyboard);
-
- if (wl->pointer)
- wl_pointer_destroy(wl->pointer);
-
if (wl->presentation)
wp_presentation_destroy(wl->presentation);
@@ -2480,6 +2800,9 @@ void vo_wayland_uninit(struct vo *vo)
if (wl->viewport)
wp_viewport_destroy(wl->viewport);
+ if (wl->cursor_viewport)
+ wp_viewport_destroy(wl->cursor_viewport);
+
if (wl->osd_viewport)
wp_viewport_destroy(wl->osd_viewport);
@@ -2492,9 +2815,6 @@ void vo_wayland_uninit(struct vo *vo)
if (wl->dmabuf_feedback)
zwp_linux_dmabuf_feedback_v1_destroy(wl->dmabuf_feedback);
- if (wl->seat)
- wl_seat_destroy(wl->seat);
-
if (wl->shm)
wl_shm_destroy(wl->shm);
@@ -2536,16 +2856,14 @@ void vo_wayland_uninit(struct vo *vo)
if (wl->xkb_context)
xkb_context_unref(wl->xkb_context);
- if (wl->xkb_keymap)
- xkb_keymap_unref(wl->xkb_keymap);
-
- if (wl->xkb_state)
- xkb_state_unref(wl->xkb_state);
-
- struct vo_wayland_output *output, *tmp;
- wl_list_for_each_safe(output, tmp, &wl->output_list, link)
+ struct vo_wayland_output *output, *output_tmp;
+ wl_list_for_each_safe(output, output_tmp, &wl->output_list, link)
remove_output(output);
+ struct vo_wayland_seat *seat, *seat_tmp;
+ wl_list_for_each_safe(seat, seat_tmp, &wl->seat_list, link)
+ remove_seat(seat);
+
if (wl->display)
wl_display_disconnect(wl->display);
diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h
index adbcca6..7a2f319 100644
--- a/video/out/wayland_common.h
+++ b/video/out/wayland_common.h
@@ -22,6 +22,8 @@
#include "input/event.h"
#include "vo.h"
+struct vo_wayland_seat;
+
typedef struct {
uint32_t format;
uint32_t padding;
@@ -64,18 +66,18 @@ struct vo_wayland_state {
int bounded_width;
int reduced_height;
int reduced_width;
- int toplevel_width;
- int toplevel_height;
/* State */
bool activated;
- bool configured;
bool focused;
bool frame_wait;
- bool has_keyboard_input;
+ bool geometry_configured;
bool hidden;
bool initial_size_hint;
bool locked_size;
+ bool need_rescale;
+ bool reconfigured;
+ bool scale_configured;
bool state_change;
bool tiled;
bool toplevel_configured;
@@ -83,6 +85,7 @@ struct vo_wayland_state {
int mouse_x;
int mouse_y;
int pending_vo_events;
+ double pending_scaling;
double scaling;
int timeout_count;
int wakeup_pipe[2];
@@ -96,7 +99,6 @@ struct vo_wayland_state {
/* cursor-shape */
/* TODO: unvoid these if required wayland protocols is bumped to 1.32+ */
void *cursor_shape_manager;
- void *cursor_shape_device;
/* fractional-scale */
/* TODO: unvoid these if required wayland protocols is bumped to 1.31+ */
@@ -112,7 +114,6 @@ struct vo_wayland_state {
struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback;
wayland_format *format_map;
uint32_t format_size;
- bool using_dmabuf_wayland;
/* presentation-time */
struct wp_presentation *presentation;
@@ -138,26 +139,18 @@ struct vo_wayland_state {
/* viewporter */
struct wp_viewporter *viewporter;
struct wp_viewport *viewport;
+ struct wp_viewport *cursor_viewport;
struct wp_viewport *osd_viewport;
struct wp_viewport *video_viewport;
/* Input */
- struct wl_keyboard *keyboard;
- struct wl_pointer *pointer;
- struct wl_seat *seat;
- struct wl_touch *touch;
+ struct wl_list seat_list;
struct xkb_context *xkb_context;
- struct xkb_keymap *xkb_keymap;
- struct xkb_state *xkb_state;
- uint32_t keyboard_code;
- int mpkey;
- int mpmod;
/* DND */
- struct wl_data_device *dnd_ddev;
struct wl_data_device_manager *dnd_devman;
struct wl_data_offer *dnd_offer;
- enum mp_dnd_action dnd_action;
+ int dnd_action; // actually enum mp_dnd_action
char *dnd_mime_type;
int dnd_fd;
int dnd_mime_score;
@@ -168,7 +161,7 @@ struct vo_wayland_state {
struct wl_surface *cursor_surface;
bool cursor_visible;
int allocated_cursor_scale;
- uint32_t pointer_id;
+ struct vo_wayland_seat *last_button_seat;
};
bool vo_wayland_check_visible(struct vo *vo);
@@ -178,7 +171,7 @@ bool vo_wayland_reconfig(struct vo *vo);
int vo_wayland_allocate_memfd(struct vo *vo, size_t size);
int vo_wayland_control(struct vo *vo, int *events, int request, void *arg);
-void vo_wayland_handle_fractional_scale(struct vo_wayland_state *wl);
+void vo_wayland_handle_scale(struct vo_wayland_state *wl);
void vo_wayland_set_opaque_region(struct vo_wayland_state *wl, bool alpha);
void vo_wayland_sync_swap(struct vo_wayland_state *wl);
void vo_wayland_uninit(struct vo *vo);
diff --git a/video/out/win32/droptarget.c b/video/out/win32/droptarget.c
index 8a33522..fdf9c7a 100644
--- a/video/out/win32/droptarget.c
+++ b/video/out/win32/droptarget.c
@@ -18,6 +18,7 @@
#include <windows.h>
#include <ole2.h>
+#include <shellapi.h>
#include <shobjidl.h>
#include "common/msg.h"
@@ -156,8 +157,10 @@ static STDMETHODIMP DropTarget_Drop(IDropTarget *self, IDataObject *pDataObj,
wchar_t *buf = talloc_array(NULL, wchar_t, len + 1);
if (DragQueryFileW(drop, i, buf, len + 1) == len) {
- char *fname = mp_to_utf8(files, buf);
+ wchar_t *target = mp_w32_get_shell_link_target(buf);
+ char *fname = mp_to_utf8(files, target ? target : buf);
files[recvd_files++] = fname;
+ talloc_free(target);
MP_VERBOSE(t, "received dropped file: %s\n", fname);
} else {
diff --git a/video/out/win32/menu.c b/video/out/win32/menu.c
new file mode 100644
index 0000000..25681e8
--- /dev/null
+++ b/video/out/win32/menu.c
@@ -0,0 +1,231 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <windows.h>
+#include <string.h>
+
+#include "libmpv/client.h"
+#include "osdep/io.h"
+#include "mpv_talloc.h"
+
+#include "menu.h"
+
+struct menu_ctx {
+ HMENU menu;
+ void *ta_data; // talloc context for MENUITEMINFOW.dwItemData
+};
+
+// append menu item to HMENU
+static int append_menu(HMENU hmenu, UINT fMask, UINT fType, UINT fState,
+ wchar_t *title, HMENU submenu, void *data)
+{
+ static UINT id = WM_USER + 100;
+ MENUITEMINFOW mii = {0};
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | fMask;
+ mii.wID = id++;
+
+ if (fMask & MIIM_FTYPE)
+ mii.fType = fType;
+ if (fMask & MIIM_STATE)
+ mii.fState = fState;
+ if (fMask & MIIM_STRING) {
+ mii.dwTypeData = title;
+ mii.cch = wcslen(title);
+ }
+ if (fMask & MIIM_SUBMENU)
+ mii.hSubMenu = submenu;
+ if (fMask & MIIM_DATA)
+ mii.dwItemData = (ULONG_PTR)data;
+
+ return InsertMenuItemW(hmenu, -1, TRUE, &mii) ? mii.wID : -1;
+}
+
+// build fState for menu item creation
+static int build_state(mpv_node *node)
+{
+ int fState = 0;
+ for (int i = 0; i < node->u.list->num; i++) {
+ mpv_node *item = &node->u.list->values[i];
+ if (item->format != MPV_FORMAT_STRING)
+ continue;
+
+ if (strcmp(item->u.string, "hidden") == 0) {
+ return -1;
+ } else if (strcmp(item->u.string, "checked") == 0) {
+ fState |= MFS_CHECKED;
+ } else if (strcmp(item->u.string, "disabled") == 0) {
+ fState |= MFS_DISABLED;
+ }
+ }
+ return fState;
+}
+
+// build dwTypeData for menu item creation
+static wchar_t *build_title(void *talloc_ctx, char *title, char *shortcut)
+{
+ if (shortcut && shortcut[0]) {
+ char *buf = talloc_asprintf(NULL, "%s\t%s", title, shortcut);
+ wchar_t *wbuf = mp_from_utf8(talloc_ctx, buf);
+ talloc_free(buf);
+ return wbuf;
+ }
+ return mp_from_utf8(talloc_ctx, title);
+}
+
+// build HMENU from mpv node
+//
+// node structure:
+//
+// MPV_FORMAT_NODE_ARRAY
+// MPV_FORMAT_NODE_MAP (menu item)
+// "type" MPV_FORMAT_STRING
+// "title" MPV_FORMAT_STRING
+// "cmd" MPV_FORMAT_STRING
+// "shortcut" MPV_FORMAT_STRING
+// "state" MPV_FORMAT_NODE_ARRAY[MPV_FORMAT_STRING]
+// "submenu" MPV_FORMAT_NODE_ARRAY[menu item]
+static void build_menu(void *talloc_ctx, HMENU hmenu, struct mpv_node *node)
+{
+ if (node->format != MPV_FORMAT_NODE_ARRAY)
+ return;
+
+ for (int i = 0; i < node->u.list->num; i++) {
+ mpv_node *item = &node->u.list->values[i];
+ if (item->format != MPV_FORMAT_NODE_MAP)
+ continue;
+
+ mpv_node_list *list = item->u.list;
+
+ char *type = "";
+ char *title = NULL;
+ char *cmd = NULL;
+ char *shortcut = NULL;
+ int fState = 0;
+ HMENU submenu = NULL;
+
+ for (int j = 0; j < list->num; j++) {
+ char *key = list->keys[j];
+ mpv_node *value = &list->values[j];
+
+ switch (value->format) {
+ case MPV_FORMAT_STRING:
+ if (strcmp(key, "title") == 0) {
+ title = value->u.string;
+ } else if (strcmp(key, "cmd") == 0) {
+ cmd = value->u.string;
+ } else if (strcmp(key, "type") == 0) {
+ type = value->u.string;
+ } else if (strcmp(key, "shortcut") == 0) {
+ shortcut = value->u.string;
+ }
+ break;
+ case MPV_FORMAT_NODE_ARRAY:
+ if (strcmp(key, "state") == 0) {
+ fState = build_state(value);
+ } else if (strcmp(key, "submenu") == 0) {
+ submenu = CreatePopupMenu();
+ build_menu(talloc_ctx, submenu, value);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (fState == -1) // hidden
+ continue;
+
+ if (strcmp(type, "separator") == 0) {
+ append_menu(hmenu, MIIM_FTYPE, MFT_SEPARATOR, 0, NULL, NULL, NULL);
+ } else {
+ if (title == NULL || title[0] == '\0')
+ continue;
+
+ UINT fMask = MIIM_STRING | MIIM_STATE;
+ bool grayed = false;
+ if (strcmp(type, "submenu") == 0) {
+ if (submenu == NULL)
+ submenu = CreatePopupMenu();
+ fMask |= MIIM_SUBMENU;
+ grayed = GetMenuItemCount(submenu) == 0;
+ } else {
+ fMask |= MIIM_DATA;
+ grayed = cmd == NULL || cmd[0] == '\0' || cmd[0] == '#' ||
+ strcmp(cmd, "ignore") == 0;
+ }
+ int id = append_menu(hmenu, fMask, 0, (UINT)fState,
+ build_title(talloc_ctx, title, shortcut),
+ submenu, talloc_strdup(talloc_ctx, cmd));
+ if (id > 0 && grayed)
+ EnableMenuItem(hmenu, id, MF_BYCOMMAND | MF_GRAYED);
+ }
+ }
+}
+
+struct menu_ctx *mp_win32_menu_init(void)
+{
+ struct menu_ctx *ctx = talloc_ptrtype(NULL, ctx);
+ ctx->menu = CreatePopupMenu();
+ ctx->ta_data = talloc_new(ctx);
+ return ctx;
+}
+
+void mp_win32_menu_uninit(struct menu_ctx *ctx)
+{
+ DestroyMenu(ctx->menu);
+ talloc_free(ctx);
+}
+
+void mp_win32_menu_show(struct menu_ctx *ctx, HWND hwnd)
+{
+ POINT pt;
+ RECT rc;
+
+ if (!GetCursorPos(&pt))
+ return;
+
+ GetClientRect(hwnd, &rc);
+ ScreenToClient(hwnd, &pt);
+
+ if (!PtInRect(&rc, pt))
+ return;
+
+ ClientToScreen(hwnd, &pt);
+ TrackPopupMenuEx(ctx->menu, TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y,
+ hwnd, NULL);
+}
+
+void mp_win32_menu_update(struct menu_ctx *ctx, struct mpv_node *data)
+{
+ while (GetMenuItemCount(ctx->menu) > 0)
+ RemoveMenu(ctx->menu, 0, MF_BYPOSITION);
+ talloc_free_children(ctx->ta_data);
+
+ build_menu(ctx->ta_data, ctx->menu, data);
+}
+
+const char* mp_win32_menu_get_cmd(struct menu_ctx *ctx, UINT id)
+{
+ MENUITEMINFOW mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_DATA;
+
+ GetMenuItemInfoW(ctx->menu, id, FALSE, &mii);
+ return (const char *)mii.dwItemData;
+}
diff --git a/osdep/macosx_menubar.h b/video/out/win32/menu.h
index 509083d..8b1fe72 100644
--- a/osdep/macosx_menubar.h
+++ b/video/out/win32/menu.h
@@ -15,16 +15,18 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MPV_MACOSX_MENU
-#define MPV_MACOSX_MENU
+#ifndef MP_WIN32_MENU_H
+#define MP_WIN32_MENU_H
-// Menu Keys identifying menu items
-typedef enum {
- MPM_H_SIZE,
- MPM_N_SIZE,
- MPM_D_SIZE,
- MPM_MINIMIZE,
- MPM_ZOOM,
-} MPMenuKey;
+#include <windows.h>
-#endif /* MPV_MACOSX_MENU */
+struct mpv_node;
+struct menu_ctx;
+
+struct menu_ctx *mp_win32_menu_init(void);
+void mp_win32_menu_uninit(struct menu_ctx *ctx);
+void mp_win32_menu_show(struct menu_ctx *ctx, HWND hwnd);
+void mp_win32_menu_update(struct menu_ctx *ctx, struct mpv_node *data);
+const char* mp_win32_menu_get_cmd(struct menu_ctx *ctx, UINT id);
+
+#endif
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
index b4605bf..fa2f2ba 100644
--- a/video/out/x11_common.c
+++ b/video/out/x11_common.c
@@ -29,6 +29,7 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
+#include <X11/Xresource.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <X11/XF86keysym.h>
@@ -98,7 +99,9 @@
#define MWM_FUNC_MAXIMIZE (1L << 4)
#define MWM_FUNC_CLOSE (1L << 5)
-#define MWM_DECOR_ALL (1L << 0)
+// Equals to all MWM_DECOR_* OR'd together.
+#define MWM_DECOR_ALL 126
+#define MWM_DECOR_TITLE (1L << 3)
typedef struct
{
@@ -603,6 +606,70 @@ static void vo_x11_get_bounding_monitors(struct vo_x11_state *x11, long b[4])
}
}
+// Get the dpi scale of the x11 screen. Almost no GUI programs use this value
+// nowadays, and it has inconsistent behavior for different drivers.
+// (it returns the physical display DPI for proprietary NVIDIA driver only,
+// but essentially a user-set prefrence value everywhere else)
+static void vo_x11_get_x11_screen_dpi_scale(struct vo_x11_state *x11)
+{
+ int w_mm = DisplayWidthMM(x11->display, x11->screen);
+ int h_mm = DisplayHeightMM(x11->display, x11->screen);
+ double dpi_x = x11->ws_width * 25.4 / w_mm;
+ double dpi_y = x11->ws_height * 25.4 / h_mm;
+ double base_dpi = 96;
+ if (isfinite(dpi_x) && isfinite(dpi_y)) {
+ int s_x = lrint(MPCLAMP(2 * dpi_x / base_dpi, 0, 20));
+ int s_y = lrint(MPCLAMP(2 * dpi_y / base_dpi, 0, 20));
+ if (s_x == s_y && s_x > 2 && s_x < 20) {
+ x11->dpi_scale = s_x / 2.0;
+ MP_VERBOSE(x11, "Using X11 screen DPI scale: %g", x11->dpi_scale);
+ }
+ }
+}
+
+// Get the dpi scale from the Xft.dpi resource. In practice, this value is much more
+// commonly used by GUI programs for scaling compared to the x11 screen dpi.
+// This is always a preference value so it's also more consistent.
+static bool vo_x11_get_xft_dpi_scale(struct vo_x11_state *x11)
+{
+ XrmInitialize();
+ char *resman = XResourceManagerString(x11->display);
+ if (!resman)
+ return false;
+
+ XrmDatabase db = XrmGetStringDatabase(resman);
+ if (!db)
+ return false;
+
+ XrmValue ret;
+ char *type;
+ double base_dpi = 96;
+ bool success = false;
+ if (XrmGetResource(db, "Xft.dpi", "String", &type, &ret) == True &&
+ ret.addr && !strcmp("String", type))
+ {
+ char *end;
+ long value = strtol(ret.addr, &end, 10);
+ if (*ret.addr && *end == '\0') {
+ int s = lrint(MPCLAMP(2 * value / base_dpi, 0, 20));
+ if (s > 2 && s < 20) {
+ x11->dpi_scale = s / 2.0;
+ MP_VERBOSE(x11, "Using Xft.dpi scale: %g", x11->dpi_scale);
+ success = true;
+ }
+ }
+ }
+ XrmDestroyDatabase(db);
+ return success;
+}
+
+static void vo_x11_get_dpi_scale(struct vo_x11_state *x11)
+{
+ if (!vo_x11_get_xft_dpi_scale(x11))
+ vo_x11_get_x11_screen_dpi_scale(x11);
+ x11->pending_vo_events |= VO_EVENT_DPI;
+}
+
bool vo_x11_init(struct vo *vo)
{
char *dispName;
@@ -667,21 +734,7 @@ bool vo_x11_init(struct vo *vo)
x11->ws_width, x11->ws_height, dispName,
x11->display_is_local ? "local" : "remote");
- int w_mm = DisplayWidthMM(x11->display, x11->screen);
- int h_mm = DisplayHeightMM(x11->display, x11->screen);
- double dpi_x = x11->ws_width * 25.4 / w_mm;
- double dpi_y = x11->ws_height * 25.4 / h_mm;
- double base_dpi = 96;
- if (isfinite(dpi_x) && isfinite(dpi_y) && x11->opts->hidpi_window_scale) {
- int s_x = lrint(MPCLAMP(dpi_x / base_dpi, 0, 10));
- int s_y = lrint(MPCLAMP(dpi_y / base_dpi, 0, 10));
- if (s_x == s_y && s_x > 1 && s_x < 10) {
- x11->dpi_scale = s_x;
- MP_VERBOSE(x11, "Assuming DPI scale %d for prescaling. This can "
- "be disabled with --hidpi-window-scale=no.\n",
- x11->dpi_scale);
- }
- }
+ vo_x11_get_dpi_scale(x11);
x11->wm_type = vo_wm_detect(vo);
@@ -754,7 +807,8 @@ static const struct mp_keymap keymap[] = {
{XF86XK_HomePage, MP_KEY_HOMEPAGE}, {XF86XK_WWW, MP_KEY_WWW},
{XF86XK_Mail, MP_KEY_MAIL}, {XF86XK_Favorites, MP_KEY_FAVORITES},
{XF86XK_Search, MP_KEY_SEARCH}, {XF86XK_Sleep, MP_KEY_SLEEP},
- {XF86XK_Back, MP_KEY_BACK}, {XF86XK_Tools, MP_KEY_TOOLS},
+ {XF86XK_Back, MP_KEY_GO_BACK}, {XF86XK_Forward, MP_KEY_GO_FORWARD},
+ {XF86XK_Tools, MP_KEY_TOOLS},
{XF86XK_ZoomIn, MP_KEY_ZOOMIN}, {XF86XK_ZoomOut, MP_KEY_ZOOMOUT},
{0, 0}
@@ -783,7 +837,7 @@ static int vo_x11_lookupkey(int key)
return mpkey;
}
-static void vo_x11_decoration(struct vo *vo, bool d)
+static void vo_x11_decoration(struct vo *vo, bool decorations, bool title_bar)
{
struct vo_x11_state *x11 = vo->x11;
@@ -794,8 +848,9 @@ static void vo_x11_decoration(struct vo *vo, bool d)
MotifWmHints mhints = {0};
bool got = x11_get_property_copy(x11, x11->window, motif_hints,
motif_hints, 32, &mhints, sizeof(mhints));
- // hints weren't set, and decorations requested -> assume WM displays them
- if (!got && d)
+ // If hints weren't set, and decorations and title bar requested,
+ // assume WM displays them.
+ if (!got && decorations && title_bar)
return;
if (!got) {
mhints.flags = MWM_HINTS_FUNCTIONS;
@@ -803,7 +858,8 @@ static void vo_x11_decoration(struct vo *vo, bool d)
MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE;
}
mhints.flags |= MWM_HINTS_DECORATIONS;
- mhints.decorations = d ? MWM_DECOR_ALL : 0;
+ mhints.decorations = decorations ? MWM_DECOR_ALL : 0;
+ mhints.decorations &= ~(!title_bar ? MWM_DECOR_TITLE : 0);
XChangeProperty(x11->display, x11->window, motif_hints, motif_hints, 32,
PropModeReplace, (unsigned char *) &mhints, 5);
}
@@ -1105,11 +1161,6 @@ static void vo_x11_check_net_wm_state_change(struct vo *vo)
XFree(elems);
}
- if (opts->window_maximized && !is_maximized && x11->geometry_change) {
- x11->geometry_change = false;
- vo_x11_config_vo_window(vo);
- }
-
opts->window_minimized = is_minimized;
x11->hidden = is_minimized;
m_config_cache_write_opt(x11->opts_cache, &opts->window_minimized);
@@ -1163,7 +1214,28 @@ static void release_all_keys(struct vo *vo)
if (x11->no_autorepeat)
mp_input_put_key(x11->input_ctx, MP_INPUT_RELEASE_ALL);
- x11->win_drag_button1_down = false;
+}
+
+static void vo_x11_begin_dragging(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ XEvent Event = x11->last_button_event;
+ if (Event.type == ButtonPress && !x11->fs &&
+ !mp_input_test_dragging(x11->input_ctx, Event.xmotion.x,
+ Event.xmotion.y))
+ {
+ mp_input_put_key(x11->input_ctx, MP_INPUT_RELEASE_ALL);
+ XUngrabPointer(x11->display, CurrentTime);
+
+ long params[5] = {
+ Event.xmotion.x_root, Event.xmotion.y_root,
+ 8, // _NET_WM_MOVERESIZE_MOVE
+ Event.xbutton.button,
+ 1, // source indication: normal
+ };
+ x11_send_ewmh_msg(x11, "_NET_WM_MOVERESIZE", params);
+ x11->last_button_event = (XEvent){0};
+ }
}
void vo_x11_check_events(struct vo *vo)
@@ -1176,6 +1248,8 @@ void vo_x11_check_events(struct vo *vo)
while (XPending(display)) {
XNextEvent(display, &Event);
+ if (XFilterEvent(&Event, x11->window))
+ continue;
MP_TRACE(x11, "XEvent: %d\n", Event.type);
switch (Event.type) {
case Expose:
@@ -1232,30 +1306,12 @@ void vo_x11_check_events(struct vo *vo)
release_all_keys(vo);
break;
case MotionNotify:
- if (x11->win_drag_button1_down && !x11->fs &&
- !mp_input_test_dragging(x11->input_ctx, Event.xmotion.x,
- Event.xmotion.y))
- {
- mp_input_put_key(x11->input_ctx, MP_INPUT_RELEASE_ALL);
- XUngrabPointer(x11->display, CurrentTime);
-
- long params[5] = {
- Event.xmotion.x_root, Event.xmotion.y_root,
- 8, // _NET_WM_MOVERESIZE_MOVE
- 1, // button 1
- 1, // source indication: normal
- };
- x11_send_ewmh_msg(x11, "_NET_WM_MOVERESIZE", params);
- } else {
- mp_input_set_mouse_pos(x11->input_ctx, Event.xmotion.x,
- Event.xmotion.y);
- }
- x11->win_drag_button1_down = false;
+ mp_input_set_mouse_pos(x11->input_ctx, Event.xmotion.x,
+ Event.xmotion.y);
break;
case LeaveNotify:
if (Event.xcrossing.mode != NotifyNormal)
break;
- x11->win_drag_button1_down = false;
mp_input_put_key(x11->input_ctx, MP_KEY_MOUSE_LEAVE);
break;
case EnterNotify:
@@ -1266,22 +1322,20 @@ void vo_x11_check_events(struct vo *vo)
case ButtonPress:
if (Event.xbutton.button - 1 >= MP_KEY_MOUSE_BTN_COUNT)
break;
- if (Event.xbutton.button == 1)
- x11->win_drag_button1_down = true;
mp_input_put_key(x11->input_ctx,
(MP_MBTN_BASE + Event.xbutton.button - 1) |
get_mods(Event.xbutton.state) | MP_KEY_STATE_DOWN);
long msg[4] = {XEMBED_REQUEST_FOCUS};
vo_x11_xembed_send_message(x11, msg);
+ x11->last_button_event = Event;
break;
case ButtonRelease:
if (Event.xbutton.button - 1 >= MP_KEY_MOUSE_BTN_COUNT)
break;
- if (Event.xbutton.button == 1)
- x11->win_drag_button1_down = false;
mp_input_put_key(x11->input_ctx,
(MP_MBTN_BASE + Event.xbutton.button - 1) |
get_mods(Event.xbutton.state) | MP_KEY_STATE_UP);
+ x11->last_button_event = Event;
break;
case MapNotify:
x11->window_hidden = false;
@@ -1343,6 +1397,7 @@ void vo_x11_check_events(struct vo *vo)
}
if (Event.type == x11->xrandr_event) {
xrandr_read(x11);
+ vo_x11_get_dpi_scale(x11);
vo_x11_update_geometry(vo);
}
break;
@@ -1372,8 +1427,7 @@ static void vo_x11_sizehint(struct vo *vo, struct mp_rect rc, bool override_pos)
override_pos; // for fullscreen and such
XSizeHints *hint = XAllocSizeHints();
- if (!hint)
- return; // OOM
+ MP_HANDLE_OOM(hint);
hint->flags |= PSize | (force_pos ? PPosition : 0);
hint->x = rc.x0;
@@ -1579,7 +1633,7 @@ static void vo_x11_create_window(struct vo *vo, XVisualInfo *vis,
if (x11->xim) {
x11->xic = XCreateIC(x11->xim,
- XNInputStyle, XIMPreeditNone | XIMStatusNone,
+ XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, x11->window,
XNFocusWindow, x11->window,
NULL);
@@ -1601,7 +1655,7 @@ static void vo_x11_map_window(struct vo *vo, struct mp_rect rc)
struct vo_x11_state *x11 = vo->x11;
vo_x11_move_resize(vo, true, true, rc);
- vo_x11_decoration(vo, x11->opts->border);
+ vo_x11_decoration(vo, x11->opts->border, x11->opts->title_bar);
if (x11->opts->fullscreen && (x11->wm_type & vo_wm_FULLSCREEN)) {
Atom state = XA(x11, _NET_WM_STATE_FULLSCREEN);
@@ -1667,12 +1721,12 @@ static void vo_x11_map_window(struct vo *vo, struct mp_rect rc)
vo_x11_xembed_update(x11, XEMBED_MAPPED);
}
-static void vo_x11_highlevel_resize(struct vo *vo, struct mp_rect rc)
+static void vo_x11_highlevel_resize(struct vo *vo, struct mp_rect rc, bool force)
{
struct vo_x11_state *x11 = vo->x11;
struct mp_vo_opts *opts = x11->opts;
- bool reset_pos = opts->force_window_position;
+ bool reset_pos = opts->force_window_position || force;
if (reset_pos) {
x11->nofsrc = rc;
} else {
@@ -1742,10 +1796,6 @@ void vo_x11_config_vo_window(struct vo *vo)
assert(x11->window);
- // Don't attempt to change autofit/geometry on maximized windows.
- if (x11->geometry_change && opts->window_maximized)
- return;
-
vo_x11_update_screeninfo(vo);
struct vo_win_geometry geo;
@@ -1761,15 +1811,19 @@ void vo_x11_config_vo_window(struct vo *vo)
bool reset_size = (x11->old_dw != RC_W(rc) || x11->old_dh != RC_H(rc)) &&
(opts->auto_window_resize || x11->geometry_change);
+ reset_size |= (x11->old_x != rc.x0 || x11->old_y != rc.y0) &&
+ (x11->geometry_change);
x11->old_dw = RC_W(rc);
x11->old_dh = RC_H(rc);
+ x11->old_x = rc.x0;
+ x11->old_y = rc.y0;
if (x11->window_hidden) {
x11->nofsrc = rc;
vo_x11_map_window(vo, rc);
} else if (reset_size) {
- vo_x11_highlevel_resize(vo, rc);
+ vo_x11_highlevel_resize(vo, rc, x11->geometry_change);
}
x11->geometry_change = false;
@@ -1922,7 +1976,7 @@ static void vo_x11_fullscreen(struct vo *vo)
rc = x11->screenrc;
}
- vo_x11_decoration(vo, opts->border && !x11->fs);
+ vo_x11_decoration(vo, opts->border && !x11->fs, opts->title_bar);
vo_x11_sizehint(vo, rc, true);
XMoveResizeWindow(x11->display, x11->window, rc.x0, rc.y0,
@@ -2020,8 +2074,8 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
vo_x11_fullscreen(vo);
if (opt == &opts->ontop)
vo_x11_setlayer(vo, opts->ontop);
- if (opt == &opts->border)
- vo_x11_decoration(vo, opts->border);
+ if (opt == &opts->border || opt == &opts->title_bar)
+ vo_x11_decoration(vo, opts->border, opts->title_bar);
if (opt == &opts->all_workspaces)
vo_x11_sticky(vo, opts->all_workspaces);
if (opt == &opts->window_minimized)
@@ -2035,6 +2089,12 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
if (opt == &opts->geometry || opt == &opts->autofit ||
opt == &opts->autofit_smaller || opt == &opts->autofit_larger)
{
+ if (opts->window_maximized && !opts->fullscreen) {
+ x11->opts->window_maximized = false;
+ m_config_cache_write_opt(x11->opts_cache,
+ &x11->opts->window_maximized);
+ vo_x11_maximize(vo);
+ }
vo_x11_set_geometry(vo);
}
}
@@ -2044,16 +2104,16 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
int *s = arg;
if (!x11->window || x11->parent)
return VO_FALSE;
- s[0] = (x11->fs ? RC_W(x11->nofsrc) : RC_W(x11->winrc)) / x11->dpi_scale;
- s[1] = (x11->fs ? RC_H(x11->nofsrc) : RC_H(x11->winrc)) / x11->dpi_scale;
+ s[0] = x11->fs ? RC_W(x11->nofsrc) : RC_W(x11->winrc);
+ s[1] = x11->fs ? RC_H(x11->nofsrc) : RC_H(x11->winrc);
return VO_TRUE;
}
case VOCTRL_SET_UNFS_WINDOW_SIZE: {
int *s = arg;
if (!x11->window || x11->parent)
return VO_FALSE;
- int w = s[0] * x11->dpi_scale;
- int h = s[1] * x11->dpi_scale;
+ int w = s[0];
+ int h = s[1];
struct mp_rect rc = x11->winrc;
rc.x1 = rc.x0 + w;
rc.y1 = rc.y0 + h;
@@ -2063,7 +2123,7 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
&x11->opts->window_maximized);
vo_x11_maximize(vo);
}
- vo_x11_highlevel_resize(vo, rc);
+ vo_x11_highlevel_resize(vo, rc, false);
if (!x11->fs) { // guess new window size, instead of waiting for X
x11->winrc.x1 = x11->winrc.x0 + w;
x11->winrc.y1 = x11->winrc.y0 + h;
@@ -2151,6 +2211,9 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
case VOCTRL_GET_HIDPI_SCALE:
*(double *)arg = x11->dpi_scale;
return VO_TRUE;
+ case VOCTRL_BEGIN_DRAGGING:
+ vo_x11_begin_dragging(vo);
+ return VO_TRUE;
}
return VO_NOTIMPL;
}
diff --git a/video/out/x11_common.h b/video/out/x11_common.h
index 62a96d7..b8ebfe0 100644
--- a/video/out/x11_common.h
+++ b/video/out/x11_common.h
@@ -61,7 +61,7 @@ struct vo_x11_state {
int display_is_local;
int ws_width;
int ws_height;
- int dpi_scale;
+ double dpi_scale;
struct mp_rect screenrc;
char *window_title;
@@ -114,6 +114,7 @@ struct vo_x11_state {
* stays the same (even if that size is different from the current
* window size after the user modified the latter). */
int old_dw, old_dh;
+ int old_x, old_y;
/* Video size changed during fullscreen when we couldn't tell the new
* size to the window manager. Must set window size when turning
* fullscreen off. */
@@ -137,10 +138,9 @@ struct vo_x11_state {
Atom dnd_requested_action;
Window dnd_src_window;
- /* dragging the window */
- bool win_drag_button1_down;
-
Atom icc_profile_property;
+
+ XEvent last_button_event;
};
bool vo_x11_init(struct vo *vo);