diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst index 6177d09a40..dafd13b7a7 100644 --- a/DOCS/interface-changes.rst +++ b/DOCS/interface-changes.rst @@ -46,6 +46,10 @@ Interface changes need to explictly add `start` depending on how you have `--watch-later-options` configured. - add `--vd-lavc-dr=auto` and make it the default + - add support for the fractional scale protocol in wayland + - in wayland, hidpi window scaling now scales the window by the compositor's + dpi scale factor by default (can be disabled with --no-hidpi-window-scale + if fractional scaling support exists). --- mpv 0.35.0 --- - add the `--vo=gpu-next` video output driver, as well as the options `--allow-delayed-peak-detect`, `--builtin-scalers`, diff --git a/generated/wayland/meson.build b/generated/wayland/meson.build index 423fc7c76e..3680346319 100644 --- a/generated/wayland/meson.build +++ b/generated/wayland/meson.build @@ -16,6 +16,11 @@ if features['wayland_protocols_1_27'] [wl_protocol_dir, 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml']] endif +features += {'wayland_protocols_1_31': wayland['deps'][2].version().version_compare('>=1.31')} +if features['wayland_protocols_1_31'] + protocols += [[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml']] +endif + foreach p: protocols xml = join_paths(p) wl_protocols_source += custom_target(xml.underscorify() + '_c', diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c index a932ca48fc..0380114e95 100644 --- a/video/out/opengl/context_wayland.c +++ b/video/out/opengl/context_wayland.c @@ -84,6 +84,8 @@ static void resize(struct ra_ctx *ctx) 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/vo_wlshm.c b/video/out/vo_wlshm.c index 6f70767b29..44983465da 100644 --- a/video/out/vo_wlshm.c +++ b/video/out/vo_wlshm.c @@ -204,6 +204,9 @@ static int resize(struct vo *vo) p->free_buffers = buf->next; talloc_free(buf); } + + vo_wayland_handle_fractional_scale(wl); + return mp_sws_reinit(p->sws); } diff --git a/video/out/vulkan/context_wayland.c b/video/out/vulkan/context_wayland.c index 157c76855c..5ca6265a91 100644 --- a/video/out/vulkan/context_wayland.c +++ b/video/out/vulkan/context_wayland.c @@ -118,6 +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); return ra_vk_ctx_resize(ctx, width, height); } diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index aa987480c3..e2ba998956 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -47,6 +47,10 @@ #include "generated/wayland/single-pixel-buffer-v1.h" #endif +#if HAVE_WAYLAND_PROTOCOLS_1_31 +#include "generated/wayland/fractional-scale-v1.h" +#endif + #if WAYLAND_VERSION_MAJOR > 1 || WAYLAND_VERSION_MINOR >= 20 #define HAVE_WAYLAND_1_20 #endif @@ -181,6 +185,7 @@ 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 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); static void set_surface_scaling(struct vo_wayland_state *wl); static void window_move(struct vo_wayland_state *wl, uint32_t serial); @@ -753,7 +758,7 @@ static void surface_handle_enter(void *data, struct wl_surface *wl_surface, wl->current_output->has_surface = true; bool force_resize = false; - if (wl->scaling != wl->current_output->scale) { + if (!wl->fractional_scale_manager && wl->scaling != wl->current_output->scale) { set_surface_scaling(wl); spawn_cursor(wl); force_resize = true; @@ -768,7 +773,7 @@ static void surface_handle_enter(void *data, struct wl_surface *wl_surface, if (!mp_rect_equals(&old_geometry, &wl->geometry) || force_resize) wl->pending_vo_events |= VO_EVENT_RESIZE; - MP_VERBOSE(wl, "Surface entered output %s %s (0x%x), scale = %i, refresh rate = %f Hz\n", + 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->pending_vo_events |= VO_EVENT_WIN_STATE; @@ -964,6 +969,32 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = { #endif }; +#if HAVE_WAYLAND_PROTOCOLS_1_31 +static void preferred_scale(void *data, + struct wp_fractional_scale_v1 *fractional_scale, + uint32_t scale) +{ + struct vo_wayland_state *wl = data; + double old_scale = wl->scaling; + + // dmabuf_wayland is always wl->scaling = 1 + bool dmabuf_wayland = !strcmp(wl->vo->driver->name, "dmabuf-wayland"); + wl->scaling = !dmabuf_wayland ? (double)scale / 120 : 1; + 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); + wl->pending_vo_events |= VO_EVENT_RESIZE; + } +} + +static const struct wp_fractional_scale_v1_listener fractional_scale_listener = { + preferred_scale, +}; +#endif + static const char *zxdg_decoration_mode_to_str(const uint32_t mode) { switch (mode) { @@ -1225,6 +1256,12 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id } #endif +#if HAVE_WAYLAND_PROTOCOLS_1_31 + 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 (!strcmp(interface, wp_presentation_interface.name) && found++) { wl->presentation = wl_registry_bind(reg, id, &wp_presentation_interface, 1); wp_presentation_add_listener(wl->presentation, &pres_listener, wl); @@ -1363,6 +1400,20 @@ static bool create_input(struct vo_wayland_state *wl) return 0; } +static int create_viewports(struct vo_wayland_state *wl) +{ + if (wl->viewporter) { + wl->viewport = wp_viewporter_get_viewport(wl->viewporter, wl->surface); + wl->video_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->video_surface); + } + + if (wl->viewporter && (!wl->viewport || !wl->video_viewport)) { + MP_ERR(wl, "failed to create viewport interfaces!\n"); + return 1; + } + return 0; +} + static int create_xdg_surface(struct vo_wayland_state *wl) { wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->wm_base, wl->surface); @@ -1371,8 +1422,10 @@ static int create_xdg_surface(struct vo_wayland_state *wl) wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface); xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl); - if (!wl->xdg_surface || !wl->xdg_toplevel) + if (!wl->xdg_surface || !wl->xdg_toplevel) { + MP_ERR(wl, "failled to create xdg_surface and xdg_toplevel!\n"); return 1; + } return 0; } @@ -1514,6 +1567,15 @@ static void request_decoration_mode(struct vo_wayland_state *wl, uint32_t mode) zxdg_toplevel_decoration_v1_set_mode(wl->xdg_toplevel_decoration, mode); } +static void rescale_geometry(struct vo_wayland_state *wl, double old_scale) +{ + double factor = old_scale / wl->scaling; + wl->window_size.x1 /= factor; + wl->window_size.y1 /= factor; + wl->geometry.x1 /= factor; + wl->geometry.y1 /= factor; +} + static void clean_feedback_pool(struct vo_wayland_feedback_pool *fback_pool) { for (int i = 0; i < fback_pool->len; ++i) { @@ -1623,20 +1685,19 @@ 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) + return; + + // dmabuf_wayland is always wl->scaling = 1 bool dmabuf_wayland = !strcmp(wl->vo->driver->name, "dmabuf-wayland"); - int old_scale = wl->scaling; + double old_scale = wl->scaling; if (wl->vo_opts->hidpi_window_scale && !dmabuf_wayland) { wl->scaling = wl->current_output->scale; } else { wl->scaling = 1; } - double factor = (double)old_scale / wl->scaling; - wl->window_size.x1 /= factor; - wl->window_size.y1 /= factor; - wl->geometry.x1 /= factor; - wl->geometry.y1 /= factor; - + rescale_geometry(wl, old_scale); wl_surface_set_buffer_scale(wl->surface, wl->scaling); } @@ -1862,7 +1923,6 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg) opt == &opts->autofit_smaller || opt == &opts->autofit_larger) { if (wl->current_output) { - set_geometry(wl); if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized) wl->geometry = wl->window_size; wl->pending_vo_events |= VO_EVENT_RESIZE; @@ -1950,6 +2010,14 @@ 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) +{ + 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)); +} + bool vo_wayland_init(struct vo *vo) { vo->wl = talloc_zero(NULL, struct vo_wayland_state); @@ -2004,6 +2072,9 @@ bool vo_wayland_init(struct vo *vo) } /* Can't be initialized during registry due to multi-protocol dependence */ + if (create_viewports(wl)) + goto err; + if (create_xdg_surface(wl)) goto err; @@ -2012,11 +2083,6 @@ bool vo_wayland_init(struct vo *vo) wl_subsurface_set_desync(wl->video_subsurface); } - if (wl->viewporter) { - wl->viewport = wp_viewporter_get_viewport(wl->viewporter, wl->surface); - wl->video_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->video_surface); - } - #if HAVE_WAYLAND_PROTOCOLS_1_27 if (wl->content_type_manager) { wl->content_type = wp_content_type_manager_v1_get_surface_content_type(wl->content_type_manager, wl->surface); @@ -2031,6 +2097,16 @@ bool vo_wayland_init(struct vo *vo) } #endif +#if HAVE_WAYLAND_PROTOCOLS_1_31 + if (wl->fractional_scale_manager) { + wl->fractional_scale = wp_fractional_scale_manager_v1_get_fractional_scale(wl->fractional_scale_manager, wl->surface); + wp_fractional_scale_v1_add_listener(wl->fractional_scale, &fractional_scale_listener, wl); + } else { + MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n", + wp_fractional_scale_manager_v1_interface.name); + } +#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); @@ -2210,6 +2286,14 @@ void vo_wayland_uninit(struct vo *vo) if (wl->fback_pool) clean_feedback_pool(wl->fback_pool); +#if HAVE_WAYLAND_PROTOCOLS_1_31 + if (wl->fractional_scale) + wp_fractional_scale_v1_destroy(wl->fractional_scale); + + if (wl->fractional_scale_manager) + wp_fractional_scale_manager_v1_destroy(wl->fractional_scale_manager); +#endif + if (wl->frame_callback) wl_callback_destroy(wl->frame_callback); diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h index c073ae4250..fa65e80ac2 100644 --- a/video/out/wayland_common.h +++ b/video/out/wayland_common.h @@ -73,7 +73,7 @@ struct vo_wayland_state { int mouse_x; int mouse_y; int pending_vo_events; - int scaling; + double scaling; int timeout_count; int wakeup_pipe[2]; @@ -83,6 +83,11 @@ struct vo_wayland_state { void *content_type; int current_content_type; + /* fractional-scale */ + /* TODO: unvoid these if required wayland protocols is bumped to 1.31+ */ + void *fractional_scale_manager; + void *fractional_scale; + /* idle-inhibit */ struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; struct zwp_idle_inhibitor_v1 *idle_inhibitor; @@ -156,6 +161,7 @@ bool vo_wayland_supported_format(struct vo *vo, uint32_t format, uint64_t modifi 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_set_opaque_region(struct vo_wayland_state *wl, int alpha); void vo_wayland_sync_swap(struct vo_wayland_state *wl); void vo_wayland_uninit(struct vo *vo); diff --git a/wscript b/wscript index 470501d062..f03380a6c4 100644 --- a/wscript +++ b/wscript @@ -540,6 +540,11 @@ video_output_features = [ 'desc': 'wayland-protocols version 1.27+', 'deps': 'wayland', 'func': check_pkg_config('wayland-protocols >= 1.27'), + } , { + 'name': 'wayland-protocols-1-31', + 'desc': 'wayland-protocols version 1.31+', + 'deps': 'wayland', + 'func': check_pkg_config('wayland-protocols >= 1.31'), } , { 'name': 'memfd_create', 'desc': "Linux's memfd_create()", diff --git a/wscript_build.py b/wscript_build.py index b35ae069c3..4ae1ad3dee 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -154,6 +154,15 @@ def build(ctx): protocol = "staging/single-pixel-buffer/single-pixel-buffer-v1", target = "generated/wayland/single-pixel-buffer-v1.h") + + if ctx.dependency_satisfied('wayland-protocols-1-31'): + ctx.wayland_protocol_code(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "staging/fractional-scale/fractional-scale-v1", + target = "generated/wayland/fractional-scale-v1.c") + ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR, + protocol = "staging/fractional-scale/fractional-scale-v1", + target = "generated/wayland/fractional-scale-v1.h") + ctx(features = "ebml_header", target = "generated/ebml_types.h") ctx(features = "ebml_definitions", target = "generated/ebml_defs.inc") @@ -557,6 +566,7 @@ def build(ctx): ( "video/out/w32_common.c", "win32-desktop" ), ( "generated/wayland/single-pixel-buffer-v1.c", "wayland-protocols-1-27" ), ( "generated/wayland/content-type-v1.c", "wayland-protocols-1-27" ), + ( "generated/wayland/fractional-scale-v1.c", "wayland-protocols-1-31"), ( "generated/wayland/idle-inhibit-unstable-v1.c", "wayland" ), ( "generated/wayland/presentation-time.c", "wayland" ), ( "generated/wayland/xdg-decoration-unstable-v1.c", "wayland" ),