summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml29
-rw-r--r--DOCS/interface-changes.rst8
-rw-r--r--DOCS/man/ao.rst8
-rw-r--r--DOCS/man/console.rst4
-rw-r--r--DOCS/man/input.rst28
-rw-r--r--DOCS/man/libmpv.rst4
-rw-r--r--DOCS/man/mpv.rst24
-rw-r--r--DOCS/man/options.rst122
-rw-r--r--DOCS/man/vf.rst2
-rw-r--r--DOCS/man/vo.rst30
-rw-r--r--README.md6
-rwxr-xr-xTOOLS/appveyor-install.sh22
-rwxr-xr-xTOOLS/matroska.py8
-rwxr-xr-xTOOLS/umpv4
-rw-r--r--VERSION2
-rw-r--r--appveyor.yml16
-rw-r--r--audio/filter/af_scaletempo2_internals.c22
-rwxr-xr-xbootstrap.py11
-rw-r--r--common/msg.c4
-rw-r--r--demux/demux.c20
-rw-r--r--demux/demux.h2
-rw-r--r--demux/demux_mf.c2
-rw-r--r--filters/f_auto_filters.c4
-rw-r--r--filters/f_auto_filters.h2
-rw-r--r--input/cmd.c13
-rw-r--r--options/options.c2
-rw-r--r--options/options.h2
-rw-r--r--osdep/macos/libmpv_helper.swift68
-rw-r--r--osdep/macos/mpv_helper.swift36
-rw-r--r--osdep/macos/swift_compat.swift27
-rw-r--r--osdep/macosx_application.h6
-rw-r--r--osdep/macosx_application.m2
-rw-r--r--osdep/macosx_menubar.m7
-rw-r--r--osdep/macosx_touchbar.h1
-rw-r--r--osdep/macosx_touchbar.m248
-rw-r--r--osdep/terminal-unix.c10
-rw-r--r--player/command.c41
-rw-r--r--player/core.h3
-rw-r--r--player/loadfile.c17
-rw-r--r--player/lua/auto_profiles.lua16
-rw-r--r--player/lua/console.lua12
-rw-r--r--stream/stream_lavf.c8
-rw-r--r--video/csputils.c11
-rw-r--r--video/csputils.h1
-rw-r--r--video/decode/vd_lavc.c4
-rw-r--r--video/out/cocoa_cb_common.swift7
-rw-r--r--video/out/gpu/shader_cache.c20
-rw-r--r--video/out/gpu/shader_cache.h4
-rw-r--r--video/out/gpu/video.c47
-rw-r--r--video/out/gpu/video_shaders.c85
-rw-r--r--video/out/hwdec/hwdec_vaapi_vk.c4
-rw-r--r--video/out/mac/common.swift107
-rw-r--r--video/out/mac/title_bar.swift4
-rw-r--r--video/out/mac/window.swift49
-rw-r--r--video/out/opengl/context_wayland.c76
-rw-r--r--video/out/placebo/ra_pl.c83
-rw-r--r--video/out/vo_sixel.c215
-rw-r--r--video/out/vo_tct.c10
-rw-r--r--video/out/vo_wlshm.c40
-rw-r--r--video/out/vulkan/context_wayland.c76
-rw-r--r--video/out/wayland_common.c261
-rw-r--r--video/out/wayland_common.h1
-rw-r--r--video/out/x11_common.c41
-rw-r--r--video/out/x11_common.h4
-rw-r--r--video/zimg.c1
-rw-r--r--waftools/checks/custom.py24
-rw-r--r--wscript14
67 files changed, 1233 insertions, 859 deletions
diff --git a/.travis.yml b/.travis.yml
index dcffe94346..0ab0e75f5e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,15 +34,15 @@ _mingwbase:
matrix:
include:
- <<: *mac
- osx_image: xcode11.6
+ osx_image: xcode12.2
- <<: *mac
- osx_image: xcode10.1
+ osx_image: xcode11.3
- <<: *mac
- osx_image: xcode9.2
+ osx_image: xcode9.4
env:
- HOMEBREW_NO_AUTO_UPDATE=1
- HOMEBREW_NO_INSTALL_CLEANUP=1
- - CI_SWIFT_FLAGS="\-target x86_64-apple-macosx10.12"
+ - CI_HOMEBREW_HASH=7242872d7878f1a4c2706e5837faafcf0782b58d
- os: freebsd
compiler: clang
- os: linux
@@ -57,7 +57,7 @@ matrix:
env: CI_SCRIPT=ci/build-mingw64.sh TARGET=x86_64-w64-mingw32
allow_failures:
- os: osx
- osx_image: xcode9.2
+ osx_image: xcode9.4
fast_finish: true
dist: focal
@@ -118,23 +118,30 @@ before_install:
fi
- |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
- remove=$(brew list)
+ if [[ "$TRAVIS_OSX_IMAGE" == "xcode12.2" ]]; then
+ remove=$(brew list --formula)
+ else
+ remove=$(brew list)
+ fi
keep="gettext pcre2 git"
install="autoconf automake pkg-config libtool python freetype fribidi little-cms2 luajit libass ffmpeg"
for formula in ${keep[@]}; do remove=("${remove[@]/$formula}"); done
for formula in ${install[@]}; do remove=("${remove[@]/$formula}"); done
brew remove --force $remove --ignore-dependencies
- if [[ "$TRAVIS_OSX_IMAGE" == "xcode9.2" ]]; then
- brew untap caskroom/cask
+ if [[ "$TRAVIS_OSX_IMAGE" == "xcode12.2" ]]; then
+ brew remove $(brew list --cask)
+ else
+ brew cask remove $(brew cask list)
fi
+ brew untap homebrew/cask
brew update
- if [[ "$TRAVIS_OSX_IMAGE" == "xcode9.2" ]]; then
+ if [[ -n "$CI_HOMEBREW_HASH" ]]; then
pushd "/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core"
- git checkout --force 55e02323812604add9a69bab8730319b9255a697
+ git checkout --force $CI_HOMEBREW_HASH
fi
brew install $install
brew link --overwrite python
- if [[ "$TRAVIS_OSX_IMAGE" == "xcode9.2" ]]; then
+ if [[ -n "$CI_HOMEBREW_HASH" ]]; then
git checkout master
popd
fi
diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst
index 16f570ae13..1f5fda5b02 100644
--- a/DOCS/interface-changes.rst
+++ b/DOCS/interface-changes.rst
@@ -26,6 +26,14 @@ Interface changes
::
+ --- mpv 0.34.0 ---
+ - add `--screen-name` and `--fs-screen-name` flags to allow selecting the
+ screen by its name instead of the index
+ - add `--macos-geometry-calculation` to change the rectangle used for screen
+ position and size calculation. the old behavior used the whole screen,
+ which didn't take the menu bar and Dock into account. The new default
+ behaviour includes both. To revert to the old behavior set this to
+ `whole`.
--- mpv 0.33.0 ---
- add `--d3d11-exclusive-fs` flag to enable D3D11 exclusive fullscreen mode
when the player enters fullscreen.
diff --git a/DOCS/man/ao.rst b/DOCS/man/ao.rst
index cc42ec23a6..d70286647f 100644
--- a/DOCS/man/ao.rst
+++ b/DOCS/man/ao.rst
@@ -65,8 +65,8 @@ Available audio output drivers are:
mode is probably not very useful, other than for debugging or when used
with fixed setups.
-``coreaudio`` (Mac OS X only)
- Native Mac OS X audio output driver using AudioUnits and the CoreAudio
+``coreaudio`` (macOS only)
+ Native macOS audio output driver using AudioUnits and the CoreAudio
sound server.
Automatically redirects to ``coreaudio_exclusive`` when playing compressed
@@ -92,8 +92,8 @@ Available audio output drivers are:
extreme care.
-``coreaudio_exclusive`` (Mac OS X only)
- Native Mac OS X audio output driver using direct device access and
+``coreaudio_exclusive`` (macOS only)
+ Native macOS audio output driver using direct device access and
exclusive mode (bypasses the sound server).
``openal``
diff --git a/DOCS/man/console.rst b/DOCS/man/console.rst
index e9a829cbc4..22725a3134 100644
--- a/DOCS/man/console.rst
+++ b/DOCS/man/console.rst
@@ -36,7 +36,7 @@ INSERT
Toggle insert mode.
Shift+INSERT
- Paste text (uses the primary selection on X11).
+ Paste text (uses the primary selection on X11 and Wayland).
TAB
Complete the command or property name at the cursor.
@@ -54,7 +54,7 @@ Ctrl+U
Delete text from the cursor to the beginning of the line.
Ctrl+V
- Paste text (uses the clipboard on X11).
+ Paste text (uses the clipboard on X11 and Wayland).
Ctrl+W
Delete text from the cursor to the beginning of the current word.
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst
index 1bcae75809..82b570c228 100644
--- a/DOCS/man/input.rst
+++ b/DOCS/man/input.rst
@@ -62,8 +62,8 @@ character), or a symbolic name (as printed by ``--input-keylist``).
command.
``<command>`` is the command itself. It consists of the command name and
-multiple (or none) commands, all separated by whitespace. String arguments
-need to be quoted with ``"``. Details see ``Flat command syntax``.
+multiple (or none) arguments, all separated by whitespace. String arguments
+should be quoted, typically with ``"``. See ``Flat command syntax``.
You can bind multiple commands to one key. For example:
@@ -162,18 +162,28 @@ a number of other places.
|
| ``<command> ::= [<prefixes>] <command_name> (<argument>)*``
-| ``<argument> ::= (<string> | " <quoted_string> ")``
+| ``<argument> ::= (<unquoted> | " <double_quoted> " | !X <custom_quoted> X!)``
``command_name`` is an unquoted string with the command name itself. See
`List of Input Commands`_ for a list.
-Arguments are separated by whitespace. This applies even to string arguments.
-For this reason, string arguments should be quoted with ``"``. If a string
-argument contains spaces or certain special characters, quoting and possibly
-escaping is mandatory, or the command cannot be parsed correctly.
+Arguments are separated by whitespaces even if the command expects only one
+argument. Arguments with whitespaces or other special characters must be quoted,
+or the command cannot be parsed correctly.
-Inside quotes, C-style escaping can be used. JSON escapes according to RFC 8259,
-minus surrogate pair escapes, should be a safe subset that can be used.
+Double quoted arguments start and end with ``"``. Custom quotes start with ``!``
+(exclamation mark) followed by any ASCII character, and end in the same pair in
+reverse order, e.g. ``!'foo'!`` or ``!-bar-!``. The final pair sequence is not
+allowed inside the string - in these examples ``'!`` and ``-!`` respectively.
+
+Custom quotes take their content literally, while inside double quotes
+JSON/C-style escaping can be used. JSON escapes according to RFC 8259, minus
+surrogate pair escapes, should be a safe subset that can be used.
+
+Note that argument parsing and property expansion happen at different stages.
+First, arguments are determined as described above, and then, where applicable,
+properties are expanded - regardless of argument quoting. However, expansion
+can still be prevented with ``$>``. See `Property Expansion`_.
Commands specified as arrays
----------------------------
diff --git a/DOCS/man/libmpv.rst b/DOCS/man/libmpv.rst
index 909a2bb447..7f5d2f5c39 100644
--- a/DOCS/man/libmpv.rst
+++ b/DOCS/man/libmpv.rst
@@ -21,8 +21,8 @@ C PLUGINS
You can write C plugins for mpv. These use the libmpv API, although they do not
use the libmpv library itself.
-Currently, they must be explicitly enabled at build time with
-``--enable-cplugins``. They are available on Linux/BSD platforms only.
+They are available on Linux/BSD platforms only and enabled by default if the
+compiler supports linking with the ``-rdynamic`` flag.
C plugins location
------------------
diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst
index be29a977d5..736c9a097d 100644
--- a/DOCS/man/mpv.rst
+++ b/DOCS/man/mpv.rst
@@ -247,16 +247,16 @@ corresponding adjustment.)
7 and 8
Adjust saturation.
-Alt+0 (and command+0 on OSX)
+Alt+0 (and command+0 on macOS)
Resize video window to half its original size.
-Alt+1 (and command+1 on OSX)
+Alt+1 (and command+1 on macOS)
Resize video window to its original size.
-Alt+2 (and command+2 on OSX)
+Alt+2 (and command+2 on macOS)
Resize video window to double its original size.
-command + f (OSX only)
+command + f (macOS only)
Toggle fullscreen (see also ``--fs``).
(The following keys are valid if you have a keyboard with multimedia keys.)
@@ -439,8 +439,8 @@ Name Meaning
``~/`` user home directory root (similar to shell, ``$HOME``)
``~~home/`` same as ``~~/``
``~~global/`` the global config path, if available (not on win32)
-``~~osxbundle/`` the OSX bundle resource path (OSX only)
-``~~desktop/`` the path to the desktop (win32, OSX)
+``~~osxbundle/`` the macOS bundle resource path (macOS only)
+``~~desktop/`` the path to the desktop (win32, macOS)
``~~exe_dir`` win32 only: the path to the directory containing the exe (for
config file purposes; ``$MPV_HOME`` overrides it)
``~~old_home`` do not use
@@ -781,9 +781,11 @@ ignored. This Lua code execution is not sandboxed.
Any variables in condition expressions can reference properties. If an
identifier is not already defined by Lua or mpv, it is interpreted as property.
-For example, ``pause`` would return the current pause status. If the variable
-name contains any ``_`` characters, they are turned into ``-``. For example,
-``playback_time`` would return the property ``playback-time``.
+For example, ``pause`` would return the current pause status. You cannot
+reference properties with ``-`` this way since that would denote a subtraction,
+but if the variable name contains any ``_`` characters, they are turned into
+``-``. For example, ``playback_time`` would return the property
+``playback-time``.
A more robust way to access properties is using ``p.property_name`` or
``get("property-name", default_value)``. The automatic variable to property
@@ -1260,7 +1262,7 @@ Currently this happens only in the following cases:
or file associations provided by desktop environments)
- if started from explorer.exe on Windows (technically, if it was started on
Windows, and all of the stdout/stderr/stdin handles are unset)
-- started out of the bundle on OSX
+- started out of the bundle on macOS
- if you manually use ``--player-operation-mode=pseudo-gui`` on the command line
This mode applies options from the builtin profile ``builtin-pseudo-gui``, but
@@ -1298,7 +1300,7 @@ Linux desktop issues
====================
This subsection describes common problems on the Linux desktop. None of these
-problems exist on systems like Windows or OSX.
+problems exist on systems like Windows or macOS.
Disabling Screensaver
---------------------
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 50ff3ec665..399701115f 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -213,7 +213,7 @@ Playback Control
Slow down or speed up playback by the factor given as parameter.
If ``--audio-pitch-correction`` (on by default) is used, playing with a
- speed higher than normal automatically inserts the ``scaletempo`` audio
+ speed higher than normal automatically inserts the ``scaletempo2`` audio
filter.
``--pause``
@@ -392,6 +392,13 @@ Playback Control
difference between the two option is that this option performs a seek on
loop, instead of reloading the file.
+ .. note::
+
+ ``--loop-file`` counts the number of times it causes the player to
+ seek to the beginning of the file, not the number of full playthroughs. This
+ means ``--loop-file=1`` will end up playing the file twice. Contrast with
+ ``--loop-playlist``, which counts the number of full playthroughs.
+
``--loop`` is an alias for this option.
``--ab-loop-a=<time>``, ``--ab-loop-b=<time>``
@@ -1196,9 +1203,9 @@ Video
:vdpau-copy: copies video back into system RAM (Linux with some GPUs only)
:vaapi: requires ``--vo=gpu`` or ``--vo=vaapi`` (Linux only)
:vaapi-copy: copies video back into system RAM (Linux with some GPUs only)
- :videotoolbox: requires ``--vo=gpu`` (OS X 10.8 and up),
+ :videotoolbox: requires ``--vo=gpu`` (macOS 10.8 and up),
or ``--vo=libmpv`` (iOS 9.0 and up)
- :videotoolbox-copy: copies video back into system RAM (OS X 10.8 or iOS 9.0 and up)
+ :videotoolbox-copy: copies video back into system RAM (macOS 10.8 or iOS 9.0 and up)
:dxva2: requires ``--vo=gpu`` with ``--gpu-context=d3d11``,
``--gpu-context=angle`` or ``--gpu-context=dxinterop``
(Windows only)
@@ -1477,11 +1484,14 @@ Video
This option is disabled if the ``--no-keepaspect`` option is used.
``--video-rotate=<0-359|no>``
- Rotate the video clockwise, in degrees. Currently supports 90° steps only.
- If ``no`` is given, the video is never rotated, even if the file has
- rotation metadata. (The rotation value is added to the rotation metadata,
- which means the value ``0`` would rotate the video according to the
- rotation metadata.)
+ Rotate the video clockwise, in degrees. If ``no`` is given, the video is
+ never rotated, even if the file has rotation metadata. (The rotation value
+ is added to the rotation metadata, which means the value ``0`` would rotate
+ the video according to the rotation metadata.)
+
+ When using hardware decoding without copy-back, only 90° steps work, while
+ software decoding and hardware decoding methods that copy the video back to
+ system memory support all values between 0 and 359.
``--video-zoom=<value>``
Adjust the video display scale factor by the given value. The parameter is
@@ -1606,7 +1616,7 @@ Video
You can get the list of allowed codecs with ``mpv --vd=help``. Remove the
prefix, e.g. instead of ``lavc:h264`` use ``h264``.
- By default, this is set to ``h264,vc1,hevc,vp9,av1``. Note that
+ By default, this is set to ``h264,vc1,hevc,vp8,vp9,av1``. Note that
the hardware acceleration special codecs like ``h264_vdpau`` are not
relevant anymore, and in fact have been removed from Libav in this form.
@@ -1739,7 +1749,7 @@ Audio
``--audio-pitch-correction=<yes|no>``
If this is enabled (default), playing with a speed different from normal
- automatically inserts the ``scaletempo`` audio filter. For details, see
+ automatically inserts the ``scaletempo2`` audio filter. For details, see
audio filter section.
``--audio-device=<name>``
@@ -2799,7 +2809,7 @@ Subtitles
``--sub-font-provider=<auto|none|fontconfig>``
Which libass font provider backend to use (default: auto). ``auto`` will
attempt to use the native font provider: fontconfig on Linux, CoreText on
- OSX, DirectWrite on Windows. ``fontconfig`` forces fontconfig, if libass
+ macOS, DirectWrite on Windows. ``fontconfig`` forces fontconfig, if libass
was built with support (if not, it behaves like ``none``).
The ``none`` font provider effectively disables system fonts. It will still
@@ -2839,6 +2849,12 @@ Window
See also ``--fs-screen``.
+``--screen-name=<string>``
+ In multi-monitor configurations, this option tells mpv which screen to
+ display the video on based on the screen name from the video backend. The
+ same caveats in the ``--screen`` option also apply here. This option is
+ ignored and does nothing if ``--screen`` is explicitly set.
+
``--fullscreen``, ``--fs``
Fullscreen playback.
@@ -2853,12 +2869,18 @@ Window
This option works properly only with window managers which
understand the EWMH ``_NET_WM_FULLSCREEN_MONITORS`` hint.
- .. admonition:: Note (OS X)
+ .. admonition:: Note (macOS)
- ``all`` does not work on OS X and will behave like ``current``.
+ ``all`` does not work on macOS and will behave like ``current``.
See also ``--screen``.
+``--fs-screen-name=<string>``
+ In multi-monitor configurations, this option tells mpv which screen to go
+ fullscreen to based on the screen name from the video backend. The same
+ caveats in the ``--fs-screen`` option also apply here. This option is
+ ignored and does nothing if ``--fs-screen`` is explicitly set.
+
``--keep-open=<yes|no|always>``
Do not terminate when playing or seeking beyond the end of the file, and
there is not next file to be played (and ``--loop`` is not used).
@@ -2956,7 +2978,7 @@ Window
Manager.
``--ontop-level=<window|system|desktop|level>``
- (OS X only)
+ (macOS only)
Sets the level of an ontop window (default: window).
:window: On top of all other windows.
@@ -2980,7 +3002,7 @@ Window
applied to window/video with size exceeding size of the screen.
``--on-all-workspaces``
- (X11 only)
+ (X11 and macOS only)
Show the video window on all virtual desktops.
``--geometry=<[W[xH]][+-x+-y][/WS]>``, ``--geometry=<x:y>``
@@ -3006,9 +3028,9 @@ Window
Generally only supported by GUI VOs. Ignored for encoding.
- .. admonition: Note (OS X)
+ .. admonition: Note (macOS)
- On Mac OS X the origin of the screen coordinate system is located on the
+ On macOS, the origin of the screen coordinate system is located on the
bottom-left corner. For instance, ``0:0`` will place the window at the
bottom-left of the screen.
@@ -3176,14 +3198,14 @@ Window
- ``--monitoraspect=16:9`` or ``--monitoraspect=1.7777``
``--hidpi-window-scale``, ``--no-hidpi-window-scale``
- (OS X, Windows, X11, and Wayland only)
+ (macOS, Windows, X11, and Wayland only)
Scale the window size according to the backing scale factor (default: yes).
On regular HiDPI resolutions the window opens with double the size but appears
- as having the same size as on non-HiDPI resolutions. This is the default OS X
- behavior.
+ as having the same size as on non-HiDPI resolutions. This is enabled by
+ default on macOS.
``--native-fs``, ``--no-native-fs``
- (OS X only)
+ (macOS only)
Uses the native fullscreen mechanism of the OS (default: yes).
``--monitorpixelaspect=<ratio>``
@@ -3216,8 +3238,8 @@ Window
``intptr_t``. mpv will create its own window, and set the wid window as
parent, like with X11.
- On OSX/Cocoa, the ID is interpreted as ``NSView*``. Pass it as value cast
- to ``intptr_t``. mpv will create its own sub-view. Because OSX does not
+ On macOS/Cocoa, the ID is interpreted as ``NSView*``. Pass it as value cast
+ to ``intptr_t``. mpv will create its own sub-view. Because macOS does not
support window embedding of foreign processes, this works only with libmpv,
and will crash when used from the command line.
@@ -3699,15 +3721,17 @@ Demuxer
``--prefetch-playlist=<yes|no>``
Prefetch next playlist entry while playback of the current entry is ending
- (default: no). This merely opens the URL of the next playlist entry as soon
- as the current URL is fully read.
+ (default: no).
+
+ This does not prefill the cache with the video data of the next URL.
+ Prefetching video data is supported only for the current playlist entry,
+ and depends on the demuxer cache settings (on by default). This merely
+ opens the URL of the next playlist entry as soon the current URL is fully
+ read.
This does **not** work with URLs resolved by the ``youtube-dl`` wrapper,
and it won't.
- This does not affect HLS (``.m3u8`` URLs) - HLS prefetching depends on the
- demuxer cache settings and is on by default.
-
This can give subtly wrong results if per-file options are used, or if
options are changed in the time window between prefetching start and next
file played.
@@ -3852,7 +3876,7 @@ Input
Support depends on the VO in use.
``--input-media-keys=<yes|no>``
- (OS X and Windows only)
+ (macOS and Windows only)
Enable/disable media keys support. Enabled by default (except for libmpv).
``--input-right-alt-gr``, ``--no-input-right-alt-gr``
@@ -5684,7 +5708,7 @@ The following video options are currently all specific to ``--vo=gpu`` and
``--deband-threshold=<0..4096>``
The debanding filter's cut-off threshold. Higher numbers increase the
debanding strength dramatically but progressively diminish image details.
- (Default 64)
+ (Default 32)
``--deband-range=<1..64>``
The debanding filter's initial radius. The radius increases linearly for
@@ -5794,7 +5818,7 @@ The following video options are currently all specific to ``--vo=gpu`` and
Deactivates the automatic graphics switching and forces the dedicated GPU.
(default: no)
- OS X only.
+ macOS only.
``--cocoa-cb-sw-renderer=<yes|no|auto>``
Use the Apple Software Renderer when using cocoa-cb (default: auto). If set
@@ -5803,14 +5827,14 @@ The following video options are currently all specific to ``--vo=gpu`` and
software renderer, and ``auto`` only falls back to the software renderer
when the usual pixel format couldn't be created.
- OS X only.
+ macOS only.
``--cocoa-cb-10bit-context=<yes|no>``
Creates a 10bit capable pixel format for the context creation (default: yes).
Instead of 8bit integer framebuffer a 16bit half-float framebuffer is
requested.
- OS X only.
+ macOS only.
``--macos-title-bar-appearance=<appearance>``
Sets the appearance of the title bar (default: auto). Not all combinations
@@ -5891,7 +5915,7 @@ The following video options are currently all specific to ``--vo=gpu`` and
1000ms since it's possible that Apple or the user changes the system
defaults. Anything higher than 1000ms though seems too long and shouldn't be
set anyway.
- OS X and cocoa-cb only
+ (macOS and cocoa-cb only)
``--macos-app-activation-policy=<regular|accessory|prohibited>``
@@ -5900,6 +5924,16 @@ The following video options are currently all specific to ``--vo=gpu`` and
macOS only.
+``--macos-geometry-calculation=<visible|whole>``
+ This changes the rectangle which is used to calculate the screen position
+ and size of the window (default: visible). ``visible`` takes the the menu
+ bar and Dock into account and the window is only positioned/sized within the
+ visible screen frame rectangle, ``whole`` takes the whole screen frame
+ rectangle and ignores the menu bar and Dock. Other previous restrictions
+ still apply, like the window can't be placed on top of the menu bar etc.
+
+ macOS only.
+
``--android-surface-size=<WxH>``
Set dimensions of the rendering surface used by the Android gpu context.
Needs to be set by the embedding application if the dimensions change during
@@ -5918,7 +5952,7 @@ The following video options are currently all specific to ``--vo=gpu`` and
auto
auto-select (default)
cocoa
- Cocoa/OS X (deprecated, use --vo=libmpv instead)
+ Cocoa/macOS (deprecated, use --vo=libmpv instead)
win
Win32/WGL
winvk
@@ -6013,7 +6047,7 @@ The following video options are currently all specific to ``--vo=gpu`` and
With ambient illuminance of 16 lux, mpv will pick the 1.0 gamma value (no
boost), and slightly increase the boost up until 1.2 for 256 lux.
- NOTE: Only implemented on OS X.
+ NOTE: Only implemented on macOS.
``--target-prim=<value>``
Specifies the primaries of the display. Video colors will be adapted to
@@ -6362,13 +6396,13 @@ The following video options are currently all specific to ``--vo=gpu`` and
black).
yes
Try to create a framebuffer with alpha component. This only makes sense
- if the video contains alpha information (which is extremely rare). May
- not be supported on all platforms. If alpha framebuffers are
- unavailable, it silently falls back on a normal framebuffer. Note that
- if you set the ``--fbo-format`` option to a non-default value, a
- format with alpha must be specified, or this won't work.
- Whether this really works depends on the windowing system and desktop
- environment.
+ if the video contains alpha information (which is extremely rare) or if
+ you make the background color transparent. May not be supported on all
+ platforms. If alpha framebuffers are unavailable, it silently falls
+ back on a normal framebuffer. Note that if you set the ``--fbo-format``
+ option to a non-default value, a format with alpha must be specified,
+ or this won't work. Whether this really works depends on the windowing
+ system and desktop environment.
no
Ignore alpha component.
@@ -6395,7 +6429,7 @@ The following video options are currently all specific to ``--vo=gpu`` and
flipping GL front and backbuffers immediately (i.e. it doesn't call it
in display-sync mode).
- On OSX this is always deactivated because it only causes performance
+ On macOS this is always deactivated because it only causes performance
problems and other regressions.
``--gpu-dumb-mode=<yes|no|auto>``
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst
index 3797ad26bd..e07360b24d 100644
--- a/DOCS/man/vf.rst
+++ b/DOCS/man/vf.rst
@@ -310,7 +310,7 @@ Available mpv-only filters are:
``<stereo-in>``
Set the stereo mode the video is assumed to be encoded in. Use
- ``--vf format:stereo-in=help`` to list all available modes. Check with
+ ``--vf=format:stereo-in=help`` to list all available modes. Check with
the ``stereo3d`` filter documentation to see what the names mean.
``<stereo-out>``
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index 833fbddb1b..d8aefd871b 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -253,7 +253,7 @@ Available video output drivers are:
quality or performance by changing the ``--fbo-format`` option to
``rgb16f``, ``rgb32f`` or ``rgb``. Known problems include Mesa/Intel not
accepting ``rgb16``, Mesa sometimes not being compiled with float texture
- support, and some OS X setups being very slow with ``rgb16`` but fast
+ support, and some macOS setups being very slow with ``rgb16`` but fast
with ``rgb32f``. If you have problems, you can also try enabling the
``--gpu-dumb-mode=yes`` option.
@@ -413,18 +413,22 @@ Available video output drivers are:
to take into account padding at the report - this only works correctly
when the overall padding per axis is smaller than the number of cells.
+ ``--vo-sixel-exit-clear=<yes|no>`` (default: yes)
+ Whether or not to clear the terminal on quit. When set to no - the last
+ sixel image stays on screen after quit, with the cursor following it.
+
Sixel image quality options:
``--vo-sixel-dither=<algo>``
Selects the dither algorithm which libsixel should apply.
Can be one of the below list as per libsixel's documentation.
- auto
- Choose diffuse type automatically
+ auto (Default)
+ Let libsixel choose the dithering method.
none
Don't diffuse
atkinson
- Diffuse with Bill Atkinson's method. (Default)
+ Diffuse with Bill Atkinson's method.
fs
Diffuse with Floyd-Steinberg method
jajuni
@@ -444,20 +448,18 @@ Available video output drivers are:
using ``no`` (at the time of writing) will slow down ``xterm``.
``--vo-sixel-reqcolors=<colors>`` (default: 256)
- Set up libsixel to use required number of colors for dynamic palette.
- This value depends on the terminal emulator as well. Xterm supports
- 256 colors. Can set this to a lower value for faster performance.
- This option has no effect if fixed palette is used.
+ Has no effect with fixed palette. Set up libsixel to use required
+ number of colors for dynamic palette. This value depends on the
+ terminal emulator as well. Xterm supports 256 colors. Can set this to
+ a lower value for faster performance.
``--vo-sixel-threshold=<threshold>`` (default: -1)
- When using a dynamic palette, defines the threshold to change the
+ Has no effect with fixed palette. Defines the threshold to change the
palette - as percentage of the number of colors, e.g. 20 will change
the palette when the number of colors changed by 20%. It's a simple
measure to reduce the number of palette changes, because it can be slow
- in some terminals (``xterm``), however, it seems that in ``mlterm`` it
- causes image corruption. The default (-1) will change the palette
- on every frame and will have better quality, and no corruption in
- ``mlterm``.
+ in some terminals (``xterm``). The default (-1) will choose a palette
+ on every frame and will have better quality.
``image``
Output each frame into an image file in the current directory. Each file
@@ -496,7 +498,7 @@ Available video output drivers are:
Specify the directory to save the image files to (default: ``./``).
``libmpv``
- For use with libmpv direct embedding. As a special case, on OS X it
+ For use with libmpv direct embedding. As a special case, on macOS it
is used like a normal VO within mpv (cocoa-cb). Otherwise useless in any
other contexts.
(See ``<mpv/render.h>``.)
diff --git a/README.md b/README.md
index 8cc3dc3bcc..3f6eee54ae 100644
--- a/README.md
+++ b/README.md
@@ -121,9 +121,9 @@ Libass dependencies (when building libass):
- gcc or clang, yasm on x86 and x86_64
- fribidi, freetype, fontconfig development headers (for libass)
-- harfbuzz (optional, required for correct rendering of combining characters,
- particularly for correct rendering of non-English text on OSX, and
- Arabic/Indic scripts on any platform)
+- harfbuzz (required for correct rendering of combining characters, particularly
+ for correct rendering of non-English text on OSX, and Arabic/Indic scripts on
+ any platform)
FFmpeg dependencies (when building FFmpeg):
diff --git a/TOOLS/appveyor-install.sh b/TOOLS/appveyor-install.sh
index 7b2473f39d..95e4f7b4ed 100755
--- a/TOOLS/appveyor-install.sh
+++ b/TOOLS/appveyor-install.sh
@@ -23,6 +23,8 @@ pacman -S --noconfirm --needed \
$MINGW_PACKAGE_PREFIX-lua51 \
$MINGW_PACKAGE_PREFIX-ninja \
$MINGW_PACKAGE_PREFIX-rubberband \
+ $MINGW_PACKAGE_PREFIX-shaderc \
+ $MINGW_PACKAGE_PREFIX-spirv-cross \
$MINGW_PACKAGE_PREFIX-uchardet \
$MINGW_PACKAGE_PREFIX-vulkan
@@ -50,23 +52,3 @@ pacman -Sc --noconfirm
--enable-schannel
make -j4 install
)
-
-# Compile shaderc
-(
- git clone --depth=1 https://github.com/google/shaderc && cd shaderc
- "$PYTHON" utils/git-sync-deps
-
- mkdir build && cd build
- cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DSHADERC_SKIP_TESTS=ON \
- -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX ..
- ninja install
-)
-
-# Compile SPIRV-Cross
-(
- git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Cross && cd SPIRV-Cross
-
- mkdir build && cd build
- cmake -GNinja -DSPIRV_CROSS_SHARED=ON -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX ..
- ninja install
-)
diff --git a/TOOLS/matroska.py b/TOOLS/matroska.py
index 2c1b751a54..d75c45a715 100755
--- a/TOOLS/matroska.py
+++ b/TOOLS/matroska.py
@@ -21,8 +21,6 @@ Can also be used to directly parse Matroska files and display their contents.
# License along with mpv. If not, see <http://www.gnu.org/licenses/>.
#
-# for compatibility with Python 2.x
-from __future__ import print_function
elements_ebml = (
'EBML, 1a45dfa3, sub', (
@@ -300,8 +298,8 @@ parse_elems(elements_ebml, 'EBML')
parse_elems(elements_matroska, 'MATROSKA')
def printf(out, *args):
- out.write(u' '.join([str(x) for x in args]))
- out.write(u'\n')
+ out.write(' '.join(str(x) for x in args))
+ out.write('\n')
def generate_C_header(out):
printf(out, '// Generated by TOOLS/matroska.py, do not edit manually')
@@ -466,8 +464,6 @@ if __name__ == "__main__":
elif sys.argv[1] == '--generate-definitions':
generate_C_definitions(sys.stdout)
else:
- if sys.version_info.major < 3:
- raise Exception("Dumping requires Python 3.")
s = open(sys.argv[1], "rb")
while 1:
start = s.tell()
diff --git a/TOOLS/umpv b/TOOLS/umpv
index 762e73a622..2044e3e926 100755
--- a/TOOLS/umpv
+++ b/TOOLS/umpv
@@ -31,8 +31,6 @@ import os
import socket
import errno
import subprocess
-import fcntl
-import stat
import string
files = sys.argv[1:]
@@ -52,7 +50,7 @@ def make_abs(filename):
if not is_url(filename):
return os.path.abspath(filename)
return filename
-files = [make_abs(f) for f in files]
+files = (make_abs(f) for f in files)
SOCK = os.path.join(os.getenv("HOME"), ".umpv_socket")
diff --git a/VERSION b/VERSION
index be386c9ede..17d545aad7 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.33.0
+0.33.0-UNKNOWN
diff --git a/appveyor.yml b/appveyor.yml
index 92f229aa3a..a385ac0364 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,3 +1,5 @@
+image: Visual Studio 2019
+
branches:
only:
- master
@@ -15,18 +17,8 @@ shallow_clone: true
test: off
install:
- # Work around age/brokenness of Appveyor's msys2 by adding the new
- # maintainer's keyring.
- - bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
- - bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- - bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- - bash -lc "pacman -U --noconfirm msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
- # Support for Ada and Objective-C was removed from MSYS2. GCC won't update if
- # these packages are installed.
- - >-
- C:\msys64\usr\bin\pacman -R --noconfirm --noprogressbar
- mingw-w64-i686-gcc-ada mingw-w64-i686-gcc-objc mingw-w64-x86_64-gcc-ada
- mingw-w64-x86_64-gcc-objc
+ # Disable checking disk space to speed up install time
+ - C:\msys64\usr\bin\sed -i 's/^CheckSpace/#CheckSpace/g' /etc/pacman.conf
# Update core packages
- C:\msys64\usr\bin\pacman -Syyuu --noconfirm --noprogressbar --ask=20
# Explicitly kill any remaining msys2 processes after core update
diff --git a/audio/filter/af_scaletempo2_internals.c b/audio/filter/af_scaletempo2_internals.c
index e348cb37a2..5eb0e6b8d9 100644
--- a/audio/filter/af_scaletempo2_internals.c
+++ b/audio/filter/af_scaletempo2_internals.c
@@ -389,10 +389,8 @@ static int write_completed_frames_to(struct mp_scaletempo2 *p,
static bool can_perform_wsola(struct mp_scaletempo2 *p)
{
- const int search_block_size = p->num_candidate_blocks
- + (p->ola_window_size - 1);
return p->target_block_index + p->ola_window_size <= p->input_buffer_frames
- && p->search_block_index + search_block_size <= p->input_buffer_frames;
+ && p->search_block_index + p->search_block_size <= p->input_buffer_frames;
}
// number of frames needed until a wsola iteration can be performed
@@ -403,6 +401,14 @@ static int frames_needed(struct mp_scaletempo2 *p)
p->search_block_index + p->search_block_size - p->input_buffer_frames));
}
+static void resize_input_buffer(struct mp_scaletempo2 *p, int size)
+{
+ if (size > p->input_buffer_size) {
+ p->input_buffer_size = size;
+ p->input_buffer = realloc_2d(p->input_buffer, p->channels, size);
+ }
+}
+
int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
uint8_t **planes, int frame_size, bool final)
{
@@ -411,7 +417,8 @@ int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
int total_fill = final ? needed : read;
if (total_fill == 0) return 0;
- assert(total_fill + p->input_buffer_frames <= p->input_buffer_size);
+ int required_size = total_fill + p->input_buffer_frames;
+ resize_input_buffer(p, required_size);
for (int i = 0; i < p->channels; ++i) {
memcpy(p->input_buffer[i] + p->input_buffer_frames,
@@ -427,11 +434,9 @@ int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
static bool target_is_within_search_region(struct mp_scaletempo2 *p)
{
- const int search_block_size = p->num_candidate_blocks + (p->ola_window_size - 1);
-
return p->target_block_index >= p->search_block_index
&& p->target_block_index + p->ola_window_size
- <= p->search_block_index + search_block_size;
+ <= p->search_block_index + p->search_block_size;
}
@@ -715,8 +720,7 @@ void mp_scaletempo2_init(struct mp_scaletempo2 *p, int channels, int rate)
p->search_block = realloc_2d(p->search_block, p->channels, p->search_block_size);
p->target_block = realloc_2d(p->target_block, p->channels, p->ola_window_size);
- p->input_buffer_size = 4 * MPMAX(p->ola_window_size, p->search_block_size);
- p->input_buffer = realloc_2d(p->input_buffer, p->channels, p->input_buffer_size);
+ resize_input_buffer(p, 4 * MPMAX(p->ola_window_size, p->search_block_size));
p->input_buffer_frames = 0;
p->energy_candidate_blocks = realloc(p->energy_candidate_blocks,
diff --git a/bootstrap.py b/bootstrap.py
index 9949183002..51b81f5e8e 100755
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -2,8 +2,8 @@
# This script simply downloads waf to the current directory
-from __future__ import print_function
import os, sys, stat, hashlib, subprocess
+from urllib.request import urlopen, URLError
WAFRELEASE = "waf-2.0.20"
WAFURLS = ["https://waf.io/" + WAFRELEASE,
@@ -20,11 +20,6 @@ if "--no-download" in sys.argv[1:]:
print("Did not find {} and no download was requested.".format(WAFRELEASE))
sys.exit(1)
-try:
- from urllib.request import urlopen, URLError
-except:
- from urllib2 import urlopen, URLError
-
waf = None
for WAFURL in WAFURLS:
@@ -32,8 +27,8 @@ for WAFURL in WAFURLS:
print("Downloading {}...".format(WAFURL))
waf = urlopen(WAFURL).read()
break
- except URLError:
- print("Download failed.")
+ except URLError as err:
+ print("Download failed! ({})".format(err))
if not waf:
print("Could not download {}.".format(WAFRELEASE))
diff --git a/common/msg.c b/common/msg.c
index 7e271d1b8b..81c7f654f3 100644
--- a/common/msg.c
+++ b/common/msg.c
@@ -126,6 +126,8 @@ static void update_loglevel(struct mp_log *log)
struct mp_log_root *root = log->root;
pthread_mutex_lock(&root->lock);
log->level = MSGL_STATUS + root->verbose; // default log level
+ if (root->really_quiet)
+ log->level = -1;
for (int n = 0; root->msg_levels && root->msg_levels[n * 2 + 0]; n++) {
if (match_mod(log->verbose_prefix, root->msg_levels[n * 2 + 0]))
log->level = mp_msg_find_level(root->msg_levels[n * 2 + 1]);
@@ -143,8 +145,6 @@ static void update_loglevel(struct mp_log *log)
if (log->root->stats_file)
log->level = MPMAX(log->level, MSGL_STATS);
log->level = MPMIN(log->level, log->max_level);
- if (root->really_quiet)
- log->level = -1;
atomic_store(&log->reload_counter, atomic_load(&log->root->reload_counter));
pthread_mutex_unlock(&root->lock);
}
diff --git a/demux/demux.c b/demux/demux.c
index e4dda43cf0..348d665d49 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -3970,6 +3970,26 @@ void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
pthread_mutex_unlock(&in->lock);
}
+// Execute a refresh seek on the given stream.
+// ref_pts has the same meaning as with demuxer_select_track()
+void demuxer_refresh_track(struct demuxer *demuxer, struct sh_stream *stream,
+ double ref_pts)
+{
+ struct demux_internal *in = demuxer->in;
+ struct demux_stream *ds = stream->ds;
+ pthread_mutex_lock(&in->lock);
+ ref_pts = MP_ADD_PTS(ref_pts, -in->ts_offset);
+ if (ds->selected) {
+ MP_VERBOSE(in, "refresh track %d\n", stream->index);
+ update_stream_selection_state(in, ds);
+ if (in->back_demuxing)
+ ds->back_seek_pos = ref_pts;
+ if (!in->after_seek)
+ initiate_refresh_seek(in, ds, ref_pts);
+ }
+ pthread_mutex_unlock(&in->lock);
+}
+
// This is for demuxer implementations only. demuxer_select_track() sets the
// logical state, while this function returns the actual state (in case the
// demuxer attempts to cache even unselected packets for track switching - this
diff --git a/demux/demux.h b/demux/demux.h
index f49e6e2a2f..80c9a80fc7 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -290,6 +290,8 @@ void demux_block_reading(struct demuxer *demuxer, bool block);
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
double ref_pts, bool selected);
+void demuxer_refresh_track(struct demuxer *demuxer, struct sh_stream *stream,
+ double ref_pts);
void demuxer_help(struct mp_log *log);
diff --git a/demux/demux_mf.c b/demux/demux_mf.c
index 161e0cb92d..424821b965 100644
--- a/demux/demux_mf.c
+++ b/demux/demux_mf.c
@@ -254,6 +254,8 @@ static const struct {
{ "jls", "ljpeg" },
{ "thm", "mjpeg" },
{ "db", "mjpeg" },
+ { "pcd", "photocd" },
+ { "pfm", "pfm" },
{ "pcx", "pcx" },
{ "png", "png" },
{ "pns", "png" },
diff --git a/filters/f_auto_filters.c b/filters/f_auto_filters.c
index a5394dd35a..5dd3a4adff 100644
--- a/filters/f_auto_filters.c
+++ b/filters/f_auto_filters.c
@@ -329,9 +329,9 @@ static void aspeed_process(struct mp_filter *f)
if (req_filter) {
if (req_filter == 1) {
- MP_VERBOSE(f, "adding scaletempo\n");
+ MP_VERBOSE(f, "adding scaletempo2\n");
p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_AUDIO,
- "scaletempo", NULL);
+ "scaletempo2", NULL);
} else if (req_filter == 2) {
MP_VERBOSE(f, "adding drop\n");
p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_AUDIO,
diff --git a/filters/f_auto_filters.h b/filters/f_auto_filters.h
index 98043c9301..f315084158 100644
--- a/filters/f_auto_filters.h
+++ b/filters/f_auto_filters.h
@@ -9,5 +9,5 @@ struct mp_filter *mp_deint_create(struct mp_filter *parent);
// Rotate according to mp_image.rotate and VO capabilities.
struct mp_filter *mp_autorotate_create(struct mp_filter *parent);
-// Insert a filter that inserts scaletempo depending on speed settings.
+// Insert a filter that inserts scaletempo2 depending on speed settings.
struct mp_filter *mp_autoaspeed_create(struct mp_filter *parent);
diff --git a/input/cmd.c b/input/cmd.c
index 692b9f5ad5..3d3b7c6587 100644
--- a/input/cmd.c
+++ b/input/cmd.c
@@ -341,6 +341,19 @@ static int pctx_read_token(struct parse_ctx *ctx, bstr *out)
}
return 1;
}
+ if (ctx->start.len > 1 && bstr_eatstart0(&ctx->str, "!")) {
+ char endquote[2] = {ctx->str.start[0], '!'};
+ ctx->str = bstr_cut(ctx->str, 1);
+ int next = bstr_find(ctx->str, (bstr){endquote, 2});
+ if (next < 0) {
+ MP_ERR(ctx, "Unterminated custom quote: ...>%.*s<.\n", BSTR_P(start));
+ return -1;
+ }
+ *out = bstr_splice(ctx->str, 0, next);
+ ctx->str = bstr_cut(ctx->str, next+2);
+ return 1;
+ }
+
return read_token(ctx->str, &ctx->str, out) ? 1 : 0;
}
diff --git a/options/options.c b/options/options.c
index a11c1114b9..8aadc87ab9 100644
--- a/options/options.c
+++ b/options/options.c
@@ -147,8 +147,10 @@ static const m_option_t mp_vo_opt_list[] = {
{"no", 0}, {"yes", 1}, {"downscale-big", 2})},
{"wid", OPT_INT64(WinID)},
{"screen", OPT_CHOICE(screen_id, {"default", -1}), M_RANGE(0, 32)},
+ {"screen-name", OPT_STRING(screen_name)},
{"fs-screen", OPT_CHOICE(fsscreen_id, {"all", -2}, {"current", -1}),
M_RANGE(0, 32)},
+ {"fs-screen-name", OPT_STRING(fsscreen_name)},
{"keepaspect", OPT_FLAG(keepaspect)},
{"keepaspect-window", OPT_FLAG(keepaspect_window)},
{"hidpi-window-scale", OPT_FLAG(hidpi_window_scale)},
diff --git a/options/options.h b/options/options.h
index b865ff8896..f28c054500 100644
--- a/options/options.h
+++ b/options/options.h
@@ -22,7 +22,9 @@ typedef struct mp_vo_opts {
bool focus_on_open;
int screen_id;
+ char *screen_name;
int fsscreen_id;
+ char *fsscreen_name;
char *winname;
char *appid;
int x11_netwm;
diff --git a/osdep/macos/libmpv_helper.swift b/osdep/macos/libmpv_helper.swift
index d1b00cf304..b140f99587 100644
--- a/osdep/macos/libmpv_helper.swift
+++ b/osdep/macos/libmpv_helper.swift
@@ -47,23 +47,26 @@ class LibmpvHelper {
}
func initRender() {
- var advanced: CInt = 1
+ let advanced: CInt = 1
let api = UnsafeMutableRawPointer(mutating: (MPV_RENDER_API_TYPE_OPENGL as NSString).utf8String)
- var pAddress = mpv_opengl_init_params(get_proc_address: getProcAddress,
+ let pAddress = mpv_opengl_init_params(get_proc_address: getProcAddress,
get_proc_address_ctx: nil,
extra_exts: nil)
- var params: [mpv_render_param] = [
- mpv_render_param(type: MPV_RENDER_PARAM_API_TYPE, data: api),
- mpv_render_param(type: MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, data: &pAddress),
- mpv_render_param(type: MPV_RENDER_PARAM_ADVANCED_CONTROL, data: &advanced),
- mpv_render_param()
- ]
-
- if (mpv_render_context_create(&mpvRenderContext, mpvHandle, &params) < 0)
- {
- log.sendError("Render context init has failed.")
- exit(1)
+
+ MPVHelper.withUnsafeMutableRawPointers([pAddress, advanced]) { (pointers: [UnsafeMutableRawPointer?]) in
+ var params: [mpv_render_param] = [
+ mpv_render_param(type: MPV_RENDER_PARAM_API_TYPE, data: api),
+ mpv_render_param(type: MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, data: pointers[0]),
+ mpv_render_param(type: MPV_RENDER_PARAM_ADVANCED_CONTROL, data: pointers[1]),
+ mpv_render_param()
+ ]
+
+ if (mpv_render_context_create(&mpvRenderContext, mpvHandle, &params) < 0) {
+ log.sendError("Render context init has failed.")
+ exit(1)
+ }
}
+
}
let getProcAddress: (@convention(c) (UnsafeMutableRawPointer?, UnsafePointer<Int8>?)
@@ -119,26 +122,29 @@ class LibmpvHelper {
deinitLock.lock()
if mpvRenderContext != nil {
var i: GLint = 0
- var flip: CInt = 1
- var skip: CInt = skip ? 1 : 0
- var ditherDepth = depth
+ let flip: CInt = 1
+ let skip: CInt = skip ? 1 : 0
+ let ditherDepth = depth
glGetIntegerv(GLenum(GL_DRAW_FRAMEBUFFER_BINDING), &i)
// CAOpenGLLayer has ownership of FBO zero yet can return it to us,
// so only utilize a newly received FBO ID if it is nonzero.
fbo = i != 0 ? i : fbo
- var data = mpv_opengl_fbo(fbo: Int32(fbo),
+ let data = mpv_opengl_fbo(fbo: Int32(fbo),
w: Int32(surface.width),
h: Int32(surface.height),
internal_format: 0)
- var params: [mpv_render_param] = [
- mpv_render_param(type: MPV_RENDER_PARAM_OPENGL_FBO, data: &data),
- mpv_render_param(type: MPV_RENDER_PARAM_FLIP_Y, data: &flip),
- mpv_render_param(type: MPV_RENDER_PARAM_DEPTH, data: &ditherDepth),
- mpv_render_param(type: MPV_RENDER_PARAM_SKIP_RENDERING, data: &skip),
- mpv_render_param()
- ]
- mpv_render_context_render(mpvRenderContext, &params);
+
+ MPVHelper.withUnsafeMutableRawPointers([data, flip, ditherDepth, skip]) { (pointers: [UnsafeMutableRawPointer?]) in
+ var params: [mpv_render_param] = [
+ mpv_render_param(type: MPV_RENDER_PARAM_OPENGL_FBO, data: pointers[0]),
+ mpv_render_param(type: MPV_RENDER_PARAM_FLIP_Y, data: pointers[1]),
+ mpv_render_param(type: MPV_RENDER_PARAM_DEPTH, data: pointers[2]),
+ mpv_render_param(type: MPV_RENDER_PARAM_SKIP_RENDERING, data: pointers[3]),
+ mpv_render_param()
+ ]
+ mpv_render_context_render(mpvRenderContext, &params);
+ }
} else {
glClearColor(0, 0, 0, 1)
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
@@ -161,16 +167,20 @@ class LibmpvHelper {
let u8Ptr = baseAddress.assumingMemoryBound(to: UInt8.self)
let iccBstr = bstrdup(nil, bstr(start: u8Ptr, len: ptr.count))
var icc = mpv_byte_array(data: iccBstr.start, size: iccBstr.len)
- let params = mpv_render_param(type: MPV_RENDER_PARAM_ICC_PROFILE, data: &icc)
- mpv_render_context_set_parameter(mpvRenderContext, params)
+ withUnsafeMutableBytes(of: &icc) { (ptr: UnsafeMutableRawBufferPointer) in
+ let params = mpv_render_param(type: MPV_RENDER_PARAM_ICC_PROFILE, data: ptr.baseAddress)
+ mpv_render_context_set_parameter(mpvRenderContext, params)
+ }
}
}
func setRenderLux(_ lux: Int) {
if mpvRenderContext == nil { return }
var light = lux
- let params = mpv_render_param(type: MPV_RENDER_PARAM_AMBIENT_LIGHT, data: &light)
- mpv_render_context_set_parameter(mpvRenderContext, params)
+ withUnsafeMutableBytes(of: &light) { (ptr: UnsafeMutableRawBufferPointer) in
+ let params = mpv_render_param(type: MPV_RENDER_PARAM_AMBIENT_LIGHT, data: ptr.baseAddress)
+ mpv_render_context_set_parameter(mpvRenderContext, params)
+ }
}
func commandAsync(_ cmd: [String?], id: UInt64 = 1) {
diff --git a/osdep/macos/mpv_helper.swift b/osdep/macos/mpv_helper.swift
index 0e207b994b..6fde975c06 100644
--- a/osdep/macos/mpv_helper.swift
+++ b/osdep/macos/mpv_helper.swift
@@ -86,17 +86,23 @@ class MPVHelper {
func setOption(fullscreen: Bool) {
optsPtr.pointee.fullscreen = fullscreen
- m_config_cache_write_opt(optsCachePtr, UnsafeMutableRawPointer(&optsPtr.pointee.fullscreen))
+ _ = withUnsafeMutableBytes(of: &optsPtr.pointee.fullscreen) { (ptr: UnsafeMutableRawBufferPointer) in
+ m_config_cache_write_opt(optsCachePtr, ptr.baseAddress)
+ }
}
func setOption(minimized: Bool) {
optsPtr.pointee.window_minimized = Int32(minimized)
- m_config_cache_write_opt(optsCachePtr, UnsafeMutableRawPointer(&optsPtr.pointee.window_minimized))
+ _ = withUnsafeMutableBytes(of: &optsPtr.pointee.window_minimized) { (ptr: UnsafeMutableRawBufferPointer) in
+ m_config_cache_write_opt(optsCachePtr, ptr.baseAddress)
+ }
}
func setOption(maximized: Bool) {
optsPtr.pointee.window_maximized = Int32(maximized)
- m_config_cache_write_opt(optsCachePtr, UnsafeMutableRawPointer(&optsPtr.pointee.window_maximized))
+ _ = withUnsafeMutableBytes(of: &optsPtr.pointee.window_maximized) { (ptr: UnsafeMutableRawBufferPointer) in
+ m_config_cache_write_opt(optsCachePtr, ptr.baseAddress)
+ }
}
func setMacOptionCallback(_ callback: swift_wakeup_cb_fn, context object: AnyObject) {
@@ -123,4 +129,28 @@ class MPVHelper {
class func bridge<T: AnyObject>(ptr: UnsafeRawPointer) -> T {
return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
}
+
+ class func withUnsafeMutableRawPointers(_ arguments: [Any],
+ pointers: [UnsafeMutableRawPointer?] = [],
+ closure: (_ pointers: [UnsafeMutableRawPointer?]) -> Void) {
+ if arguments.count > 0 {
+ let args = Array(arguments.dropFirst(1))
+ var newPtrs = pointers
+ var firstArg = arguments.first
+ withUnsafeMutableBytes(of: &firstArg) { (ptr: UnsafeMutableRawBufferPointer) in
+ newPtrs.append(ptr.baseAddress)
+ withUnsafeMutableRawPointers(args, pointers: newPtrs, closure: closure)
+ }
+
+ return
+ }
+
+ closure(pointers)
+ }
+
+ class func getPointer<T>(_ value: inout T) -> UnsafeMutableRawPointer? {
+ return withUnsafeMutableBytes(of: &value) { (ptr: UnsafeMutableRawBufferPointer) in
+ ptr.baseAddress
+ }
+ }
}
diff --git a/osdep/macos/swift_compat.swift b/osdep/macos/swift_compat.swift
index c14aa08282..24c00b0ae1 100644
--- a/osdep/macos/swift_compat.swift
+++ b/osdep/macos/swift_compat.swift
@@ -33,7 +33,6 @@ extension String {
#endif
extension NSPasteboard.PasteboardType {
-
static let fileURLCompat: NSPasteboard.PasteboardType = {
if #available(OSX 10.13, *) {
return .fileURL
@@ -53,7 +52,6 @@ extension NSPasteboard.PasteboardType {
#if !swift(>=5.0)
extension Data {
-
mutating func withUnsafeMutableBytes<Type>(_ body: (UnsafeMutableRawBufferPointer) throws -> Type) rethrows -> Type {
let dataCount = count
return try withUnsafeMutableBytes { (ptr: UnsafeMutablePointer<UInt8>) throws -> Type in
@@ -65,33 +63,8 @@ extension Data {
#if !swift(>=4.2)
extension NSDraggingInfo {
-
var draggingPasteboard: NSPasteboard {
get { return draggingPasteboard() }
}
}
#endif
-
-#if !swift(>=4.1)
-extension Array {
-
- func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] {
- return try self.flatMap(transform)
- }
-}
-
-extension Array where Element == [CGLPixelFormatAttribute] {
-
- func contains(_ obj: [CGLPixelFormatAttribute]) -> Bool {
- return self.contains(where:{ $0 == obj })
- }
-}
-
-extension NSWindow.Level {
-
- static func +(left: NSWindow.Level, right: Int) -> NSWindow.Level {
- return NSWindow.Level(left.rawValue + right)
- }
-}
-#endif
-
diff --git a/osdep/macosx_application.h b/osdep/macosx_application.h
index 9a366d81fb..05d0b07c07 100644
--- a/osdep/macosx_application.h
+++ b/osdep/macosx_application.h
@@ -21,6 +21,11 @@
#include "osdep/macosx_menubar.h"
#include "options/m_option.h"
+enum {
+ FRAME_VISIBLE = 0,
+ FRAME_WHOLE,
+};
+
struct macos_opts {
int macos_title_bar_style;
int macos_title_bar_appearance;
@@ -29,6 +34,7 @@ struct macos_opts {
int macos_fs_animation_duration;
int macos_force_dedicated_gpu;
int macos_app_activation_policy;
+ int macos_geometry_calculation;
int cocoa_cb_sw_renderer;
int cocoa_cb_10bit_context;
};
diff --git a/osdep/macosx_application.m b/osdep/macosx_application.m
index 95c6a3f953..bb8b67b575 100644
--- a/osdep/macosx_application.m
+++ b/osdep/macosx_application.m
@@ -65,6 +65,8 @@ const struct m_sub_options macos_conf = {
{"macos-force-dedicated-gpu", OPT_FLAG(macos_force_dedicated_gpu)},
{"macos-app-activation-policy", OPT_CHOICE(macos_app_activation_policy,
{"regular", 0}, {"accessory", 1}, {"prohibited", 2})},
+ {"macos-geometry-calculation", OPT_CHOICE(macos_geometry_calculation,
+ {"visible", FRAME_VISIBLE}, {"whole", FRAME_WHOLE})},
{"cocoa-cb-sw-renderer", OPT_CHOICE(cocoa_cb_sw_renderer,
{"auto", -1}, {"no", 0}, {"yes", 1})},
{"cocoa-cb-10bit-context", OPT_FLAG(cocoa_cb_10bit_context)},
diff --git a/osdep/macosx_menubar.m b/osdep/macosx_menubar.m
index 11fdda5e53..6fefcb97aa 100644
--- a/osdep/macosx_menubar.m
+++ b/osdep/macosx_menubar.m
@@ -218,6 +218,13 @@
@"target" : self,
@"cmd" : @"cycle ontop"
}],
+ [NSMutableDictionary dictionaryWithDictionary:@{
+ @"name" : @"Toggle Visibility on All Workspaces",
+ @"action" : @"cmd:",
+ @"key" : @"",
+ @"target" : self,
+ @"cmd" : @"cycle on-all-workspaces"
+ }],
#if HAVE_MACOS_TOUCHBAR
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
diff --git a/osdep/macosx_touchbar.h b/osdep/macosx_touchbar.h
index 0aa5a052bb..a03b68c0e6 100644
--- a/osdep/macosx_touchbar.h
+++ b/osdep/macosx_touchbar.h
@@ -41,5 +41,6 @@ struct mpv_event;
@property(nonatomic, retain) NSDictionary *touchbarItems;
@property(nonatomic, assign) double duration;
@property(nonatomic, assign) double position;
+@property(nonatomic, assign) int pause;
@end
diff --git a/osdep/macosx_touchbar.m b/osdep/macosx_touchbar.m
index c189e32354..ccce8f773d 100644
--- a/osdep/macosx_touchbar.m
+++ b/osdep/macosx_touchbar.m
@@ -24,6 +24,7 @@
@synthesize touchbarItems = _touchbar_items;
@synthesize duration = _duration;
@synthesize position = _position;
+@synthesize pause = _pause;
- (id)init
{
@@ -86,58 +87,25 @@
@"name": @"Time Left"
}]
};
- }
- return self;
-}
-
--(void)processEvent:(struct mpv_event *)event
-{
- switch (event->event_id) {
- case MPV_EVENT_END_FILE: {
- self.position = 0;
- self.duration = 0;
- break;
- }
- case MPV_EVENT_PROPERTY_CHANGE: {
- [self handlePropertyChange:(mpv_event_property *)event->data];
- break;
- }
- }
-}
-
--(void)handlePropertyChange:(struct mpv_event_property *)property
-{
- NSString *name = [NSString stringWithUTF8String:property->name];
- mpv_format format = property->format;
- if ([name isEqualToString:@"time-pos"] && format == MPV_FORMAT_DOUBLE) {
- self.position = *(double *)property->data;
- self.position = self.position < 0 ? 0 : self.position;
- [self updateTouchBarTimeItems];
- } else if ([name isEqualToString:@"duration"] && format == MPV_FORMAT_DOUBLE) {
- self.duration = *(double *)property->data;
- [self updateTouchBarTimeItems];
- } else if ([name isEqualToString:@"pause"] && format == MPV_FORMAT_FLAG) {
- NSButton *playButton = self.touchbarItems[play][@"view"];
- if (*(int *)property->data) {
- playButton.image = self.touchbarItems[play][@"imageAlt"];
- } else {
- playButton.image = self.touchbarItems[play][@"image"];
- }
+ [self addObserver:self forKeyPath:@"visible" options:0 context:nil];
}
+ return self;
}
- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar
makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
{
if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"slider"]) {
- NSSliderTouchBarItem *tbItem = [[NSSliderTouchBarItem alloc] initWithIdentifier:identifier];
- tbItem.slider.minValue = 0.0f;
- tbItem.slider.maxValue = 100.0f;
- tbItem.target = self;
- tbItem.action = @selector(seekbarChanged:);
+ NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
+ NSSlider *slider = [NSSlider sliderWithTarget:self action:@selector(seekbarChanged:)];
+ slider.minValue = 0.0f;
+ slider.maxValue = 100.0f;
+ tbItem.view = slider;
tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
- [self.touchbarItems[identifier] setObject:tbItem.slider forKey:@"view"];
+ [self.touchbarItems[identifier] setObject:slider forKey:@"view"];
+ [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
+ [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
return tbItem;
} else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"button"]) {
NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
@@ -146,6 +114,8 @@
tbItem.view = tbButton;
tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
[self.touchbarItems[identifier] setObject:tbButton forKey:@"view"];
+ [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
+ [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
return tbItem;
} else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"text"]) {
NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
@@ -154,12 +124,124 @@
tbItem.view = tbText;
tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
[self.touchbarItems[identifier] setObject:tbText forKey:@"view"];
+ [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
+ [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
return tbItem;
}
return nil;
}
+- (void)observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(id)object
+ change:(NSDictionary<NSKeyValueChangeKey,id> *)change
+ context:(void *)context {
+ if ([keyPath isEqualToString:@"visible"]) {
+ NSNumber *visible = [object valueForKey:@"visible"];
+ if (visible.boolValue) {
+ [self updateTouchBarTimeItems];
+ [self updatePlayButton];
+ }
+ }
+}
+
+- (void)updateTouchBarTimeItems
+{
+ if (!self.isVisible)
+ return;
+
+ [self updateSlider];
+ [self updateTimeLeft];
+ [self updateCurrentPosition];
+}
+
+- (void)updateSlider
+{
+ NSCustomTouchBarItem *tbItem = self.touchbarItems[seekBar][@"tbItem"];
+ if (!tbItem.visible)
+ return;
+
+ NSSlider *seekSlider = self.touchbarItems[seekBar][@"view"];
+
+ if (self.duration <= 0) {
+ seekSlider.enabled = NO;
+ seekSlider.doubleValue = 0;
+ } else {
+ seekSlider.enabled = YES;
+ if (!seekSlider.highlighted)
+ seekSlider.doubleValue = (self.position/self.duration)*100;
+ }
+}
+
+- (void)updateTimeLeft
+{
+ NSCustomTouchBarItem *tbItem = self.touchbarItems[timeLeft][@"tbItem"];
+ if (!tbItem.visible)
+ return;
+
+ NSTextField *timeLeftItem = self.touchbarItems[timeLeft][@"view"];
+
+ [self removeConstraintForIdentifier:timeLeft];
+ if (self.duration <= 0) {
+ timeLeftItem.stringValue = @"";
+ } else {
+ int left = (int)(floor(self.duration)-floor(self.position));
+ NSString *leftFormat = [self formatTime:left];
+ NSString *durFormat = [self formatTime:self.duration];
+ timeLeftItem.stringValue = [NSString stringWithFormat:@"-%@", leftFormat];
+ [self applyConstraintFromString:[NSString stringWithFormat:@"-%@", durFormat]
+ forIdentifier:timeLeft];
+ }
+}
+
+- (void)updateCurrentPosition
+{
+ NSCustomTouchBarItem *tbItem = self.touchbarItems[currentPosition][@"tbItem"];
+ if (!tbItem.visible)
+ return;
+
+ NSTextField *curPosItem = self.touchbarItems[currentPosition][@"view"];
+ NSString *posFormat = [self formatTime:(int)floor(self.position)];
+ curPosItem.stringValue = posFormat;
+
+ [self removeConstraintForIdentifier:currentPosition];
+ if (self.duration <= 0) {
+ [self applyConstraintFromString:[self formatTime:self.position]
+ forIdentifier:currentPosition];
+ } else {
+ NSString *durFormat = [self formatTime:self.duration];
+ [self applyConstraintFromString:durFormat forIdentifier:currentPosition];
+ }
+}
+
+- (void)updatePlayButton
+{
+ NSCustomTouchBarItem *tbItem = self.touchbarItems[play][@"tbItem"];
+ if (!self.isVisible || !tbItem.visible)
+ return;
+
+ NSButton *playButton = self.touchbarItems[play][@"view"];
+ if (self.pause) {
+ playButton.image = self.touchbarItems[play][@"imageAlt"];
+ } else {
+ playButton.image = self.touchbarItems[play][@"image"];
+ }
+}
+
+- (void)buttonAction:(NSButton *)sender
+{
+ NSString *identifier = [self getIdentifierFromView:sender];
+ [self.app queueCommand:(char *)[self.touchbarItems[identifier][@"cmd"] UTF8String]];
+}
+
+- (void)seekbarChanged:(NSSlider *)slider
+{
+ NSString *identifier = [self getIdentifierFromView:slider];
+ NSString *seek = [NSString stringWithFormat:
+ self.touchbarItems[identifier][@"cmd"], slider.doubleValue];
+ [self.app queueCommand:(char *)[seek UTF8String]];
+}
+
- (NSString *)formatTime:(int)time
{
int seconds = time % 60;
@@ -204,48 +286,6 @@
}
}
-- (void)updateTouchBarTimeItemConstrains
-{
- [self removeConstraintForIdentifier:currentPosition];
- [self removeConstraintForIdentifier:timeLeft];
-
- if (self.duration <= 0) {
- [self applyConstraintFromString:[self formatTime:self.position]
- forIdentifier:currentPosition];
- } else {
- NSString *durFormat = [self formatTime:self.duration];
-
- [self applyConstraintFromString:durFormat forIdentifier:currentPosition];
- [self applyConstraintFromString:[NSString stringWithFormat:@"-%@", durFormat]
- forIdentifier:timeLeft];
- }
-}
-
-- (void)updateTouchBarTimeItems
-{
- NSSlider *seekSlider = self.touchbarItems[seekBar][@"view"];
- NSTextField *curPosItem = self.touchbarItems[currentPosition][@"view"];
- NSTextField *timeLeftItem = self.touchbarItems[timeLeft][@"view"];
-
- if (self.duration <= 0) {
- seekSlider.enabled = NO;
- seekSlider.doubleValue = 0;
- timeLeftItem.stringValue = @"";
- }
- else {
- seekSlider.enabled = YES;
- if (!seekSlider.highlighted)
- seekSlider.doubleValue = (self.position/self.duration)*100;
- int left = (int)(floor(self.duration)-floor(self.position));
- NSString *leftFormat = [self formatTime:left];
- timeLeftItem.stringValue = [NSString stringWithFormat:@"-%@", leftFormat];
- }
- NSString *posFormat = [self formatTime:(int)floor(self.position)];
- curPosItem.stringValue = posFormat;
-
- [self updateTouchBarTimeItemConstrains];
-}
-
- (NSString *)getIdentifierFromView:(id)view
{
NSString *identifier;
@@ -255,18 +295,40 @@
return identifier;
}
-- (void)buttonAction:(NSButton *)sender
+- (void)processEvent:(struct mpv_event *)event
{
- NSString *identifier = [self getIdentifierFromView:sender];
- [self.app queueCommand:(char *)[self.touchbarItems[identifier][@"cmd"] UTF8String]];
+ switch (event->event_id) {
+ case MPV_EVENT_END_FILE: {
+ self.position = 0;
+ self.duration = 0;
+ break;
+ }
+ case MPV_EVENT_PROPERTY_CHANGE: {
+ [self handlePropertyChange:(mpv_event_property *)event->data];
+ break;
+ }
+ }
}
-- (void)seekbarChanged:(NSSliderTouchBarItem *)sender
+- (void)handlePropertyChange:(struct mpv_event_property *)property
{
- NSString *identifier = [self getIdentifierFromView:sender.slider];
- NSString *seek = [NSString stringWithFormat:
- self.touchbarItems[identifier][@"cmd"], sender.slider.doubleValue];
- [self.app queueCommand:(char *)[seek UTF8String]];
+ NSString *name = [NSString stringWithUTF8String:property->name];
+ mpv_format format = property->format;
+
+ if ([name isEqualToString:@"time-pos"] && format == MPV_FORMAT_DOUBLE) {
+ double newPosition = *(double *)property->data;
+ newPosition = newPosition < 0 ? 0 : newPosition;
+ if ((int)(floor(newPosition) - floor(self.position)) != 0) {
+ self.position = newPosition;
+ [self updateTouchBarTimeItems];
+ }
+ } else if ([name isEqualToString:@"duration"] && format == MPV_FORMAT_DOUBLE) {
+ self.duration = *(double *)property->data;
+ [self updateTouchBarTimeItems];
+ } else if ([name isEqualToString:@"pause"] && format == MPV_FORMAT_FLAG) {
+ self.pause = *(int *)property->data;
+ [self updatePlayButton];
+ }
}
@end
diff --git a/osdep/terminal-unix.c b/osdep/terminal-unix.c
index 78eb4c4618..86c3030cdd 100644
--- a/osdep/terminal-unix.c
+++ b/osdep/terminal-unix.c
@@ -45,6 +45,14 @@
// Timeout in ms after which the (normally ambiguous) ESC key is detected.
#define ESC_TIMEOUT 100
+// Timeout in ms after which the poll for input is aborted. The FG/BG state is
+// tested before every wait, and a positive value allows reactivating input
+// processing when mpv is brought to the foreground while it was running in the
+// background. In such a situation, an infinite timeout (-1) will keep mpv
+// waiting for input without realizing the terminal state changed - and thus
+// buffer all keypresses until ENTER is pressed.
+#define INPUT_TIMEOUT 1000
+
static volatile struct termios tio_orig;
static volatile int tio_orig_set;
@@ -397,7 +405,7 @@ static void *terminal_thread(void *ptr)
{ .events = POLLIN, .fd = death_pipe[0] },
{ .events = POLLIN, .fd = tty_in }
};
- int r = polldev(fds, stdin_ok ? 2 : 1, buf.len ? ESC_TIMEOUT : -1);
+ int r = polldev(fds, stdin_ok ? 2 : 1, buf.len ? ESC_TIMEOUT : INPUT_TIMEOUT);
if (fds[0].revents)
break;
if (fds[1].revents) {
diff --git a/player/command.c b/player/command.c
index 066e8be784..f28bd120be 100644
--- a/player/command.c
+++ b/player/command.c
@@ -3917,6 +3917,7 @@ static const struct property_osd_display {
{"taskbar-progress", "Progress in taskbar"},
{"snap-window", "Snap to screen edges"},
{"ontop", "Stay on top"},
+ {"on-all-workspaces", "Visibility on all workspaces"},
{"border", "Border"},
{"framedrop", "Framedrop"},
{"deinterlace", "Deinterlace"},
@@ -6471,6 +6472,25 @@ static void update_priority(struct MPContext *mpctx)
#endif
}
+static void update_track_switch(struct MPContext *mpctx, int order, int type)
+{
+ if (!mpctx->playback_initialized)
+ return;
+
+ int tid = mpctx->opts->stream_id[order][type];
+ struct track *track;
+ if (tid == -1) {
+ // If "auto" reset to default track selection
+ track = select_default_track(mpctx, order, type);
+ mark_track_selection(mpctx, order, type, -1);
+ } else {
+ track = mp_track_by_tid(mpctx, type, tid);
+ }
+ mp_switch_track_n(mpctx, order, type, track, (tid == -1) ? 0 : FLAG_MARK_SELECTION);
+ print_track_list(mpctx, "Track switched:");
+ mp_wakeup_core(mpctx);
+}
+
void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags,
bool self_update)
{
@@ -6525,6 +6545,16 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags,
mpctx->ipc_ctx = mp_init_ipc(mpctx->clients, mpctx->global);
}
+ if (opt_ptr == &opts->vo->video_driver_list) {
+ struct track *track = mpctx->current_track[0][STREAM_VIDEO];
+ uninit_video_out(mpctx);
+ reinit_video_chain(mpctx);
+ if (track)
+ reselect_demux_stream(mpctx, track, true);
+
+ mp_wakeup_core(mpctx);
+ }
+
if (flags & UPDATE_AUDIO)
reload_audio_output(mpctx);
@@ -6619,15 +6649,8 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags,
for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
for (int order = 0; order < num_ptracks[type]; order++) {
- if (opt_ptr == &opts->stream_id[order][type] &&
- mpctx->playback_initialized)
- {
- struct track *track =
- mp_track_by_tid(mpctx, type, opts->stream_id[order][type]);
- mp_switch_track_n(mpctx, order, type, track, FLAG_MARK_SELECTION);
- print_track_list(mpctx, "Track switched:");
- mp_wakeup_core(mpctx);
- }
+ if (opt_ptr == &opts->stream_id[order][type])
+ update_track_switch(mpctx, order, type);
}
}
diff --git a/player/core.h b/player/core.h
index ec154dedd0..f2f0a15a00 100644
--- a/player/core.h
+++ b/player/core.h
@@ -547,7 +547,8 @@ void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e);
void mp_play_files(struct MPContext *mpctx);
void update_demuxer_properties(struct MPContext *mpctx);
void print_track_list(struct MPContext *mpctx, const char *msg);
-void reselect_demux_stream(struct MPContext *mpctx, struct track *track);
+void reselect_demux_stream(struct MPContext *mpctx, struct track *track,
+ bool refresh_only);
void prepare_playlist(struct MPContext *mpctx, struct playlist *pl);
void autoload_external_files(struct MPContext *mpctx, struct mp_cancel *cancel);
struct track *select_default_track(struct MPContext *mpctx, int order,
diff --git a/player/loadfile.c b/player/loadfile.c
index 0adc8e351d..058be92c83 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -362,7 +362,9 @@ void update_demuxer_properties(struct MPContext *mpctx)
// Enables or disables the stream for the given track, according to
// track->selected.
-void reselect_demux_stream(struct MPContext *mpctx, struct track *track)
+// With refresh_only=true, refreshes the stream if it's enabled.
+void reselect_demux_stream(struct MPContext *mpctx, struct track *track,
+ bool refresh_only)
{
if (!track->stream)
return;
@@ -372,7 +374,10 @@ void reselect_demux_stream(struct MPContext *mpctx, struct track *track)
if (track->type == STREAM_SUB)
pts -= 10.0;
}
- demuxer_select_track(track->demuxer, track->stream, pts, track->selected);
+ if (refresh_only)
+ demuxer_refresh_track(track->demuxer, track->stream, pts);
+ else
+ demuxer_select_track(track->demuxer, track->stream, pts, track->selected);
}
static void enable_demux_thread(struct MPContext *mpctx, struct demuxer *demux)
@@ -658,14 +663,14 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
if (current->remux_sink)
close_recorder_and_error(mpctx);
current->selected = false;
- reselect_demux_stream(mpctx, current);
+ reselect_demux_stream(mpctx, current, false);
}
mpctx->current_track[order][type] = track;
if (track) {
track->selected = true;
- reselect_demux_stream(mpctx, track);
+ reselect_demux_stream(mpctx, track, false);
}
if (type == STREAM_VIDEO && order == 0) {
@@ -1341,7 +1346,7 @@ done:
if (mpctx->playback_initialized) {
for (int n = 0; n < mpctx->num_tracks; n++)
- reselect_demux_stream(mpctx, mpctx->tracks[n]);
+ reselect_demux_stream(mpctx, mpctx->tracks[n], false);
}
mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL);
@@ -1583,7 +1588,7 @@ static void play_current_file(struct MPContext *mpctx)
}
for (int n = 0; n < mpctx->num_tracks; n++)
- reselect_demux_stream(mpctx, mpctx->tracks[n]);
+ reselect_demux_stream(mpctx, mpctx->tracks[n], false);
update_demuxer_properties(mpctx);
diff --git a/player/lua/auto_profiles.lua b/player/lua/auto_profiles.lua
index 6856138d97..fba57cc74a 100644
--- a/player/lua/auto_profiles.lua
+++ b/player/lua/auto_profiles.lua
@@ -136,16 +136,20 @@ setmetatable(p, {
})
local function compile_cond(name, s)
- -- (pre 5.2 ignores the extra arguments)
- local chunk, err = load("return " .. s, "profile " .. name .. " condition",
- "t", evil_magic)
+ local code, chunkname = "return " .. s, "profile " .. name .. " condition"
+ local chunk, err
+ if setfenv then -- lua 5.1
+ chunk, err = loadstring(code, chunkname)
+ if chunk then
+ setfenv(chunk, evil_magic)
+ end
+ else -- lua 5.2
+ chunk, err = load(code, chunkname, "t", evil_magic)
+ end
if not chunk then
msg.error("Profile '" .. name .. "' condition: " .. err)
chunk = function() return false end
end
- if setfenv then
- setfenv(chunk, evil_magic)
- end
return chunk
end
diff --git a/player/lua/console.lua b/player/lua/console.lua
index a483bbe1f4..2020952840 100644
--- a/player/lua/console.lua
+++ b/player/lua/console.lua
@@ -36,6 +36,8 @@ function detect_platform()
return 'windows'
elseif mp.get_property_native('options/macos-force-dedicated-gpu', o) ~= o then
return 'macos'
+ elseif os.getenv('WAYLAND_DISPLAY') then
+ return 'wayland'
end
return 'x11'
end
@@ -609,6 +611,14 @@ function get_clipboard(clip)
if not res.error then
return res.stdout
end
+ elseif platform == 'wayland' then
+ local res = utils.subprocess({
+ args = { 'wl-paste', clip and '-n' or '-np' },
+ playback_only = false,
+ })
+ if not res.error then
+ return res.stdout
+ end
elseif platform == 'windows' then
local res = utils.subprocess({
args = { 'powershell', '-NoProfile', '-Command', [[& {
@@ -647,7 +657,7 @@ function get_clipboard(clip)
end
-- Paste text from the window-system's clipboard. 'clip' determines whether the
--- clipboard or the primary selection buffer is used (on X11 only.)
+-- clipboard or the primary selection buffer is used (on X11 and Wayland only.)
function paste(clip)
local text = get_clipboard(clip)
local before_cur = line:sub(1, cursor - 1)
diff --git a/stream/stream_lavf.c b/stream/stream_lavf.c
index e95dbc0b43..1c7fd35ae2 100644
--- a/stream/stream_lavf.c
+++ b/stream/stream_lavf.c
@@ -270,7 +270,7 @@ static int open_f(stream_t *stream)
for (int i = 0; i < sizeof(prefix) / sizeof(prefix[0]); i++)
if (!strncmp(filename, prefix[i], strlen(prefix[i])))
filename += strlen(prefix[i]);
- if (!strncmp(filename, "rtsp:", 5)) {
+ if (!strncmp(filename, "rtsp:", 5) || !strncmp(filename, "rtsps:", 6)) {
/* This is handled as a special demuxer, without a separate
* stream layer. demux_lavf will do all the real work. Note
* that libavformat doesn't even provide a protocol entry for
@@ -411,9 +411,9 @@ const stream_info_t stream_info_ffmpeg = {
.name = "ffmpeg",
.open = open_f,
.protocols = (const char *const[]){
- "rtmp", "rtsp", "http", "https", "mms", "mmst", "mmsh", "mmshttp", "rtp",
- "httpproxy", "rtmpe", "rtmps", "rtmpt", "rtmpte", "rtmpts", "srt", "srtp",
- "gopher", "data",
+ "rtmp", "rtsp", "rtsps", "http", "https", "mms", "mmst", "mmsh", "mmshttp",
+ "rtp", "httpproxy", "rtmpe", "rtmps", "rtmpt", "rtmpte", "rtmpts", "srt",
+ "srtp", "gopher", "data",
NULL },
.can_write = true,
.stream_origin = STREAM_ORIGIN_NET,
diff --git a/video/csputils.c b/video/csputils.c
index f9b6c98689..4df754a76d 100644
--- a/video/csputils.c
+++ b/video/csputils.c
@@ -103,6 +103,7 @@ const struct m_opt_choice_alternatives mp_csp_light_names[] = {
const struct m_opt_choice_alternatives mp_chroma_names[] = {
{"unknown", MP_CHROMA_AUTO},
+ {"uhd", MP_CHROMA_TOPLEFT},
{"mpeg2/4/h264",MP_CHROMA_LEFT},
{"mpeg1/jpeg", MP_CHROMA_CENTER},
{0}
@@ -188,6 +189,8 @@ enum mp_csp_prim avcol_pri_to_mp_csp_prim(int avpri)
case AVCOL_PRI_BT709: return MP_CSP_PRIM_BT_709;
case AVCOL_PRI_BT2020: return MP_CSP_PRIM_BT_2020;
case AVCOL_PRI_BT470M: return MP_CSP_PRIM_BT_470M;
+ case AVCOL_PRI_SMPTE431: return MP_CSP_PRIM_DCI_P3;
+ case AVCOL_PRI_SMPTE432: return MP_CSP_PRIM_DISPLAY_P3;
default: return MP_CSP_PRIM_AUTO;
}
}
@@ -242,6 +245,8 @@ int mp_csp_prim_to_avcol_pri(enum mp_csp_prim prim)
case MP_CSP_PRIM_BT_709: return AVCOL_PRI_BT709;
case MP_CSP_PRIM_BT_2020: return AVCOL_PRI_BT2020;
case MP_CSP_PRIM_BT_470M: return AVCOL_PRI_BT470M;
+ case MP_CSP_PRIM_DCI_P3: return AVCOL_PRI_SMPTE431;
+ case MP_CSP_PRIM_DISPLAY_P3: return AVCOL_PRI_SMPTE432;
default: return AVCOL_PRI_UNSPECIFIED;
}
}
@@ -288,6 +293,7 @@ enum mp_csp_prim mp_csp_guess_primaries(int width, int height)
enum mp_chroma_location avchroma_location_to_mp(int avloc)
{
switch (avloc) {
+ case AVCHROMA_LOC_TOPLEFT: return MP_CHROMA_TOPLEFT;
case AVCHROMA_LOC_LEFT: return MP_CHROMA_LEFT;
case AVCHROMA_LOC_CENTER: return MP_CHROMA_CENTER;
default: return MP_CHROMA_AUTO;
@@ -297,6 +303,7 @@ enum mp_chroma_location avchroma_location_to_mp(int avloc)
int mp_chroma_location_to_av(enum mp_chroma_location mploc)
{
switch (mploc) {
+ case MP_CHROMA_TOPLEFT: return AVCHROMA_LOC_TOPLEFT;
case MP_CHROMA_LEFT: return AVCHROMA_LOC_LEFT;
case MP_CHROMA_CENTER: return AVCHROMA_LOC_CENTER;
default: return AVCHROMA_LOC_UNSPECIFIED;
@@ -309,8 +316,10 @@ void mp_get_chroma_location(enum mp_chroma_location loc, int *x, int *y)
{
*x = 0;
*y = 0;
- if (loc == MP_CHROMA_LEFT)
+ if (loc == MP_CHROMA_LEFT || loc == MP_CHROMA_TOPLEFT)
*x = -1;
+ if (loc == MP_CHROMA_TOPLEFT)
+ *y = -1;
}
void mp_invert_matrix3x3(float m[3][3])
diff --git a/video/csputils.h b/video/csputils.h
index 965c313a02..323468260f 100644
--- a/video/csputils.h
+++ b/video/csputils.h
@@ -186,6 +186,7 @@ bool mp_colorspace_equal(struct mp_colorspace c1, struct mp_colorspace c2);
enum mp_chroma_location {
MP_CHROMA_AUTO,
+ MP_CHROMA_TOPLEFT, // uhd
MP_CHROMA_LEFT, // mpeg2/4, h264
MP_CHROMA_CENTER, // mpeg1, jpeg
MP_CHROMA_COUNT,
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index f965e00e5a..eae5f7a69a 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -135,7 +135,7 @@ const struct m_sub_options vd_lavc_conf = {
.framedrop = AVDISCARD_NONREF,
.dr = 1,
.hwdec_api = "no",
- .hwdec_codecs = "h264,vc1,hevc,vp9,av1",
+ .hwdec_codecs = "h264,vc1,hevc,vp8,vp9,av1",
// Maximum number of surfaces the player wants to buffer. This number
// might require adjustment depending on whatever the player does;
// for example, if vo_gpu increases the number of reference surfaces for
@@ -661,7 +661,9 @@ static void init_avctx(struct mp_filter *vd)
if (!ctx->use_hwdec && ctx->vo && lavc_param->dr) {
avctx->opaque = vd;
avctx->get_buffer2 = get_buffer2_direct;
+#if LIBAVCODEC_VERSION_MAJOR < 60
avctx->thread_safe_callbacks = 1;
+#endif
}
avctx->flags |= lavc_param->bitexact ? AV_CODEC_FLAG_BITEXACT : 0;
diff --git a/video/out/cocoa_cb_common.swift b/video/out/cocoa_cb_common.swift
index 476b4820bc..dd0738f7e3 100644
--- a/video/out/cocoa_cb_common.swift
+++ b/video/out/cocoa_cb_common.swift
@@ -83,8 +83,7 @@ class CocoaCB: Common {
}
func updateWindowSize(_ vo: UnsafeMutablePointer<vo>) {
- guard let opts: mp_vo_opts = mpv?.opts,
- let targetScreen = getScreenBy(id: Int(opts.screen_id)) ?? NSScreen.main else
+ guard let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main else
{
log.sendWarning("Couldn't update Window size, no Screen available")
return
@@ -133,11 +132,11 @@ class CocoaCB: Common {
}
override func windowSetToFullScreen() {
- layer?.update()
+ layer?.update(force: true)
}
override func windowSetToWindow() {
- layer?.update()
+ layer?.update(force: true)
}
override func windowDidUpdateFrame() {
diff --git a/video/out/gpu/shader_cache.c b/video/out/gpu/shader_cache.c
index 5e96de9c99..bf0983f5f0 100644
--- a/video/out/gpu/shader_cache.c
+++ b/video/out/gpu/shader_cache.c
@@ -458,6 +458,26 @@ void gl_sc_blend(struct gl_shader_cache *sc,
sc->params.blend_dst_alpha = blend_dst_alpha;
}
+const char *gl_sc_bvec(struct gl_shader_cache *sc, int dims)
+{
+ static const char *bvecs[] = {
+ [1] = "bool",
+ [2] = "bvec2",
+ [3] = "bvec3",
+ [4] = "bvec4",
+ };
+
+ static const char *vecs[] = {
+ [1] = "float",
+ [2] = "vec2",
+ [3] = "vec3",
+ [4] = "vec4",
+ };
+
+ assert(dims > 0 && dims < MP_ARRAY_SIZE(bvecs));
+ return sc->ra->glsl_version >= 130 ? bvecs[dims] : vecs[dims];
+}
+
static const char *vao_glsl_type(const struct ra_renderpass_input *e)
{
// pretty dumb... too dumb, but works for us
diff --git a/video/out/gpu/shader_cache.h b/video/out/gpu/shader_cache.h
index 547c6b6307..3c87513b2b 100644
--- a/video/out/gpu/shader_cache.h
+++ b/video/out/gpu/shader_cache.h
@@ -43,6 +43,10 @@ void gl_sc_uniform_mat2(struct gl_shader_cache *sc, char *name,
bool transpose, float *v);
void gl_sc_uniform_mat3(struct gl_shader_cache *sc, char *name,
bool transpose, float *v);
+
+// Return the correct bvecN() variant for using mix() in this GLSL version
+const char *gl_sc_bvec(struct gl_shader_cache *sc, int dims);
+
void gl_sc_blend(struct gl_shader_cache *sc,
enum ra_blend blend_src_rgb,
enum ra_blend blend_dst_rgb,
diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c
index 851289e281..e5a9737473 100644
--- a/video/out/gpu/video.c
+++ b/video/out/gpu/video.c
@@ -664,6 +664,11 @@ static bool gl_video_get_lut3d(struct gl_video *p, enum mp_csp_prim prim,
talloc_free(lut3d);
+ if (!p->lut_3d_texture) {
+ p->use_lut_3d = false;
+ return false;
+ }
+
return true;
}
@@ -952,9 +957,6 @@ static void init_video(struct gl_video *p)
params.w, params.h);
plane->tex = ra_tex_create(p->ra, &params);
- if (!plane->tex)
- abort(); // shit happens
-
p->use_integer_conversion |= format->ctype == RA_CTYPE_UINT;
}
}
@@ -2341,26 +2343,29 @@ static void pass_convert_yuv(struct gl_video *p)
// as per the BT.2020 specification, table 4. This is a non-linear
// transformation because (constant) luminance receives non-equal
// contributions from the three different channels.
- GLSLF("// constant luminance conversion\n");
- GLSL(color.br = color.br * mix(vec2(1.5816, 0.9936),
- vec2(1.9404, 1.7184),
- lessThanEqual(color.br, vec2(0)))
- + color.gg;)
+ GLSLF("// constant luminance conversion \n"
+ "color.br = color.br * mix(vec2(1.5816, 0.9936), \n"
+ " vec2(1.9404, 1.7184), \n"
+ " %s(lessThanEqual(color.br, vec2(0))))\n"
+ " + color.gg; \n",
+ gl_sc_bvec(p->sc, 2));
// Expand channels to camera-linear light. This shader currently just
// assumes everything uses the BT.2020 12-bit gamma function, since the
// difference between 10 and 12-bit is negligible for anything other
// than 12-bit content.
- GLSL(color.rgb = mix(color.rgb * vec3(1.0/4.5),
- pow((color.rgb + vec3(0.0993))*vec3(1.0/1.0993),
- vec3(1.0/0.45)),
- lessThanEqual(vec3(0.08145), color.rgb));)
+ GLSLF("color.rgb = mix(color.rgb * vec3(1.0/4.5), \n"
+ " pow((color.rgb + vec3(0.0993))*vec3(1.0/1.0993), \n"
+ " vec3(1.0/0.45)), \n"
+ " %s(lessThanEqual(vec3(0.08145), color.rgb))); \n",
+ gl_sc_bvec(p->sc, 3));
// Calculate the green channel from the expanded RYcB
// The BT.2020 specification says Yc = 0.2627*R + 0.6780*G + 0.0593*B
GLSL(color.g = (color.g - 0.2627*color.r - 0.0593*color.b)*1.0/0.6780;)
// Recompress to receive the R'G'B' result, same as other systems
- GLSL(color.rgb = mix(color.rgb * vec3(4.5),
- vec3(1.0993) * pow(color.rgb, vec3(0.45)) - vec3(0.0993),
- lessThanEqual(vec3(0.0181), color.rgb));)
+ GLSLF("color.rgb = mix(color.rgb * vec3(4.5), \n"
+ " vec3(1.0993) * pow(color.rgb, vec3(0.45)) - vec3(0.0993), \n"
+ " %s(lessThanEqual(vec3(0.0181), color.rgb))); \n",
+ gl_sc_bvec(p->sc, 3));
}
p->components = 3;
@@ -3246,9 +3251,11 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame,
bool has_frame = !!frame->current;
- 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);
+ if (!has_frame || !mp_rect_equals(&p->dst_rect, &target_rc)) {
+ 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);
+ }
if (p->hwdec_overlay) {
if (has_frame) {
@@ -3578,6 +3585,10 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t
timer_pool_start(p->upload_timer);
for (int n = 0; n < p->plane_count; n++) {
struct texplane *plane = &vimg->planes[n];
+ if (!plane->tex) {
+ timer_pool_stop(p->upload_timer);
+ goto error;
+ }
struct ra_tex_upload_params params = {
.tex = plane->tex,
diff --git a/video/out/gpu/video_shaders.c b/video/out/gpu/video_shaders.c
index da48929328..91c9aac865 100644
--- a/video/out/gpu/video_shaders.c
+++ b/video/out/gpu/video_shaders.c
@@ -354,9 +354,10 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
switch (trc) {
case MP_CSP_TRC_SRGB:
- GLSL(color.rgb = mix(color.rgb * vec3(1.0/12.92),
- pow((color.rgb + vec3(0.055))/vec3(1.055), vec3(2.4)),
- lessThan(vec3(0.04045), color.rgb));)
+ 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:
GLSL(color.rgb = pow(color.rgb, vec3(2.4));)
@@ -380,9 +381,10 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
GLSL(color.rgb = pow(color.rgb, vec3(2.8));)
break;
case MP_CSP_TRC_PRO_PHOTO:
- GLSL(color.rgb = mix(color.rgb * vec3(1.0/16.0),
- pow(color.rgb, vec3(1.8)),
- lessThan(vec3(0.03125), color.rgb));)
+ 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:
GLSLF("color.rgb = pow(color.rgb, vec3(1.0/%f));\n", PQ_M2);
@@ -397,16 +399,16 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
case MP_CSP_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"
- " lessThan(vec3(0.5), color.rgb));\n",
- HLG_C, HLG_A, HLG_B);
+ " %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:
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"
- " lessThanEqual(vec3(0.181), color.rgb)); \n",
- VLOG_D, VLOG_C, VLOG_B);
+ " %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:
GLSLF("color.rgb = pow(vec3(10.0), (color.rgb - vec3(%f)) * vec3(1.0/%f))\n"
@@ -417,8 +419,8 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
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"
- " lessThanEqual(vec3(%f), color.rgb)); \n",
- SLOG_Q, SLOG_P, SLOG_C, SLOG_A, SLOG_B, SLOG_K2, SLOG_Q);
+ " %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;
default:
abort();
@@ -444,10 +446,11 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
switch (trc) {
case MP_CSP_TRC_SRGB:
- GLSL(color.rgb = mix(color.rgb * vec3(12.92),
- vec3(1.055) * pow(color.rgb, vec3(1.0/2.4))
- - vec3(0.055),
- lessThanEqual(vec3(0.0031308), color.rgb));)
+ 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:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.4));)
@@ -471,9 +474,10 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.8));)
break;
case MP_CSP_TRC_PRO_PHOTO:
- GLSL(color.rgb = mix(color.rgb * vec3(16.0),
- pow(color.rgb, vec3(1.0/1.8)),
- lessThanEqual(vec3(0.001953), color.rgb));)
+ 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:
GLSLF("color.rgb *= vec3(1.0/%f);\n", 10000 / MP_REF_WHITE);
@@ -487,15 +491,15 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
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"
- " lessThan(vec3(1.0), color.rgb));\n",
- HLG_A, HLG_B, HLG_C);
+ " %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:
GLSLF("color.rgb = mix(vec3(5.6) * color.rgb + vec3(0.125), \n"
" vec3(%f) * log(color.rgb + vec3(%f)) \n"
" + vec3(%f), \n"
- " lessThanEqual(vec3(0.01), color.rgb)); \n",
- VLOG_C / M_LN10, VLOG_B, VLOG_D);
+ " %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:
GLSLF("color.rgb = vec3(%f) * log(color.rgb + vec3(%f)) + vec3(%f);\n",
@@ -505,8 +509,8 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
GLSLF("color.rgb = mix(vec3(%f) * color.rgb + vec3(%f), \n"
" vec3(%f) * log(vec3(%f) * color.rgb + vec3(%f)) \n"
" + vec3(%f), \n"
- " lessThanEqual(vec3(0.0), color.rgb)); \n",
- SLOG_P, SLOG_Q, SLOG_A / M_LN10, SLOG_K2, SLOG_B, SLOG_C);
+ " %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;
default:
abort();
@@ -537,9 +541,10 @@ static void pass_ootf(struct gl_shader_cache *sc, enum mp_csp_light light,
// This OOTF is defined by encoding the result as 709 and then decoding
// it as 1886; although this is called 709_1886 we actually use the
// more precise (by one decimal) values from BT.2020 instead
- GLSL(color.rgb = mix(color.rgb * vec3(4.5),
- vec3(1.0993) * pow(color.rgb, vec3(0.45)) - vec3(0.0993),
- lessThan(vec3(0.0181), color.rgb));)
+ GLSLF("color.rgb = mix(color.rgb * vec3(4.5), \n"
+ " vec3(1.0993) * pow(color.rgb, vec3(0.45)) - vec3(0.0993), \n"
+ " %s(lessThan(vec3(0.0181), color.rgb))); \n",
+ gl_sc_bvec(sc, 3));
GLSL(color.rgb = pow(color.rgb, vec3(2.4));)
break;
case MP_CSP_LIGHT_SCENE_1_2:
@@ -570,10 +575,11 @@ static void pass_inverse_ootf(struct gl_shader_cache *sc, enum mp_csp_light ligh
}
case MP_CSP_LIGHT_SCENE_709_1886:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.4));)
- GLSL(color.rgb = mix(color.rgb * vec3(1.0/4.5),
- pow((color.rgb + vec3(0.0993)) * vec3(1.0/1.0993),
- vec3(1/0.45)),
- lessThan(vec3(0.08145), color.rgb));)
+ GLSLF("color.rgb = mix(color.rgb * vec3(1.0/4.5), \n"
+ " pow((color.rgb + vec3(0.0993)) * vec3(1.0/1.0993), \n"
+ " vec3(1/0.45)), \n"
+ " %s(lessThan(vec3(0.08145), color.rgb))); \n",
+ gl_sc_bvec(sc, 3));
break;
case MP_CSP_LIGHT_SCENE_1_2:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/1.2));)
@@ -718,7 +724,8 @@ static void pass_tone_map(struct gl_shader_cache *sc,
"max(1e-6, sig_peak - 1.0);\n");
GLSLF("float scale = (b*b + 2.0*b*j + j*j) / (b-a);\n");
GLSLF("sig = mix(sig, scale * (sig + vec3(a)) / (sig + vec3(b)),"
- " greaterThan(sig, vec3(j)));\n");
+ " %s(greaterThan(sig, vec3(j))));\n",
+ gl_sc_bvec(sc, 3));
GLSLF("}\n");
break;
@@ -751,7 +758,8 @@ static void pass_tone_map(struct gl_shader_cache *sc,
GLSL(float scale = pow(cutoff / sig_peak, gamma.x) / cutoff;)
GLSLF("sig = mix(scale * sig,"
" pow(sig / sig_peak, vec3(gamma)),"
- " greaterThan(sig, vec3(cutoff)));\n");
+ " %s(greaterThan(sig, vec3(cutoff))));\n",
+ gl_sc_bvec(sc, 3));
break;
}
@@ -784,7 +792,8 @@ static void pass_tone_map(struct gl_shader_cache *sc,
"vec3 pb = (2.0 * tb3 - 3.0 * tb2 + vec3(1.0)) * vec3(ks) + \n"
" (tb3 - 2.0 * tb2 + tb) * vec3(1.0 - ks) + \n"
" (-2.0 * tb3 + 3.0 * tb2) * vec3(maxLum); \n"
- "sig = mix(pb, sig_pq.rgb, lessThan(sig_pq.rgb, vec3(ks))); \n");
+ "sig = mix(pb, sig_pq.rgb, %s(lessThan(sig_pq.rgb, vec3(ks)))); \n",
+ gl_sc_bvec(sc, 3));
// Convert back from PQ space to linear light
GLSLF("sig *= vec3(sig_pq.a); \n"
"sig = pow(sig, vec3(1.0/%f)); \n"
@@ -937,7 +946,7 @@ struct deband_opts {
const struct deband_opts deband_opts_def = {
.iterations = 1,
- .threshold = 64.0,
+ .threshold = 32.0,
.range = 16.0,
.grain = 48.0,
};
@@ -990,8 +999,8 @@ void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
// the difference is below the given threshold
GLSLF("avg = average(%f, h);\n", i * opts->range);
GLSL(diff = abs(color - avg);)
- GLSLF("color = mix(avg, color, greaterThan(diff, vec4(%f)));\n",
- opts->threshold / (i * 16384.0));
+ GLSLF("color = mix(avg, color, %s(greaterThan(diff, vec4(%f))));\n",
+ gl_sc_bvec(sc, 4), opts->threshold / (i * 16384.0));
}
// Add some random noise to smooth out residual differences
diff --git a/video/out/hwdec/hwdec_vaapi_vk.c b/video/out/hwdec/hwdec_vaapi_vk.c
index 1cee9e86b9..afb81be82a 100644
--- a/video/out/hwdec/hwdec_vaapi_vk.c
+++ b/video/out/hwdec/hwdec_vaapi_vk.c
@@ -43,6 +43,7 @@ static bool vaapi_vk_map(struct ra_hwdec_mapper *mapper)
int fd = p->desc.objects[id].fd;
uint32_t size = p->desc.objects[id].size;
uint32_t offset = p->desc.layers[n].offset[0];
+ uint32_t pitch = p->desc.layers[n].pitch[0];
#if PL_API_VER >= 88
// AMD drivers do not return the size in the surface description, so we
@@ -85,6 +86,9 @@ static bool vaapi_vk_map(struct ra_hwdec_mapper *mapper)
#if PL_API_VER >= 88
.drm_format_mod = p->desc.objects[id].drm_format_modifier,
#endif
+#if PL_API_VER >= 106
+ .stride_w = pitch,
+#endif
},
};
diff --git a/video/out/mac/common.swift b/video/out/mac/common.swift
index cb1b74b196..7ede81cbda 100644
--- a/video/out/mac/common.swift
+++ b/video/out/mac/common.swift
@@ -100,6 +100,7 @@ class Common: NSObject {
}
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.title = title
window.border = Bool(mpv.opts.border)
@@ -186,8 +187,7 @@ class Common: NSObject {
func startDisplayLink(_ vo: UnsafeMutablePointer<vo>) {
CVDisplayLinkCreateWithActiveCGDisplays(&link)
- guard let opts: mp_vo_opts = mpv?.opts,
- let screen = getScreenBy(id: Int(opts.screen_id)) ?? NSScreen.main,
+ 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")
@@ -409,9 +409,27 @@ class Common: NSObject {
return NSScreen.screens[screenID]
}
+ func getScreenBy(name screenName: String?) -> NSScreen? {
+ for screen in NSScreen.screens {
+ if screen.displayName == screenName {
+ return screen
+ }
+ }
+ return nil
+ }
+
func getTargetScreen(forFullscreen fs: Bool) -> NSScreen? {
- let screenID = fs ? (mpv?.opts.fsscreen_id ?? 0) : (mpv?.opts.screen_id ?? 0)
- return getScreenBy(id: Int(screenID))
+ 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
+ var name: String?
+ if let screenName = fs ? mpv.opts.fsscreen_name : mpv.opts.screen_name {
+ name = String(cString: screenName)
+ }
+ return getScreenBy(id: Int(screenID)) ?? getScreenBy(name: name)
}
func getCurrentScreen() -> NSScreen? {
@@ -423,21 +441,27 @@ class Common: NSObject {
func getWindowGeometry(forScreen targetScreen: NSScreen,
videoOut vo: UnsafeMutablePointer<vo>) -> NSRect {
let r = targetScreen.convertRectToBacking(targetScreen.frame)
- var screenRC: mp_rect = mp_rect(x0: Int32(0),
- y0: Int32(0),
- x1: Int32(r.size.width),
- y1: Int32(r.size.height))
-
+ let targetFrame =
+ (mpv?.macOpts.macos_geometry_calculation ?? Int32(FRAME_VISIBLE)) == FRAME_VISIBLE ?
+ targetScreen.visibleFrame : targetScreen.frame
+ let rv = targetScreen.convertRectToBacking(targetFrame)
+
+ // flip the y origin, mp_rect expects the origin at the top-left
+ // macOS' windowing system operates from the bottom-left
+ var originY = r.size.height - rv.origin.y - rv.size.height
+ var screenRC: mp_rect = mp_rect(x0: Int32(rv.origin.x),
+ y0: Int32(originY),
+ x1: Int32(rv.origin.x + rv.size.width),
+ y1: Int32(originY + rv.size.height))
var geo: vo_win_geometry = vo_win_geometry()
vo_calc_window_geometry2(vo, &screenRC, Double(targetScreen.backingScaleFactor), &geo)
+ vo_apply_window_geometry(vo, &geo)
- // flip y coordinates
- geo.win.y1 = Int32(r.size.height) - geo.win.y1
- geo.win.y0 = Int32(r.size.height) - geo.win.y0
-
- let wr = NSMakeRect(CGFloat(geo.win.x0), CGFloat(geo.win.y1),
- CGFloat(geo.win.x1 - geo.win.x0),
- CGFloat(geo.win.y0 - geo.win.y1))
+ // flip the y origin again
+ let height = CGFloat(geo.win.y1 - geo.win.y0)
+ originY = r.size.height - CGFloat(geo.win.y0) - height
+ let wr = NSMakeRect(CGFloat(geo.win.x0), originY,
+ CGFloat(geo.win.x1 - geo.win.x0), height)
return targetScreen.convertRectFromBacking(wr)
}
@@ -446,7 +470,7 @@ class Common: NSObject {
log.sendError("Something went wrong, no MPVHelper was initialized")
exit(1)
}
- guard let targetScreen = getScreenBy(id: Int(mpv.opts.screen_id)) ?? NSScreen.main else {
+ guard let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main else {
log.sendError("Something went wrong, no Screen was found")
exit(1)
}
@@ -508,42 +532,40 @@ class Common: NSObject {
events.pointee |= Int32(checkEvents())
return VO_TRUE
case VOCTRL_VO_OPTS_CHANGED:
- var o: UnsafeMutableRawPointer?
- while mpv.nextChangedOption(property: &o) {
- guard let opt = o else {
- log.sendError("No changed options was retrieved")
- return VO_TRUE
- }
- if opt == UnsafeMutableRawPointer(&mpv.optsPtr.pointee.border) {
+ var opt: UnsafeMutableRawPointer?
+ while mpv.nextChangedOption(property: &opt) {
+ switch opt {
+ case MPVHelper.getPointer(&mpv.optsPtr.pointee.border):
DispatchQueue.main.async {
self.window?.border = Bool(mpv.opts.border)
}
- }
- if opt == UnsafeMutableRawPointer(&mpv.optsPtr.pointee.fullscreen) {
+ case MPVHelper.getPointer(&mpv.optsPtr.pointee.fullscreen):
DispatchQueue.main.async {
self.window?.toggleFullScreen(nil)
}
- }
- if opt == UnsafeMutableRawPointer(&mpv.optsPtr.pointee.ontop) ||
- opt == UnsafeMutableRawPointer(&mpv.optsPtr.pointee.ontop_level) {
+ case MPVHelper.getPointer(&mpv.optsPtr.pointee.ontop): fallthrough
+ case MPVHelper.getPointer(&mpv.optsPtr.pointee.ontop_level):
DispatchQueue.main.async {
self.window?.setOnTop(Bool(mpv.opts.ontop), Int(mpv.opts.ontop_level))
}
- }
- if opt == UnsafeMutableRawPointer(&mpv.optsPtr.pointee.keepaspect_window) {
+ case MPVHelper.getPointer(&mpv.optsPtr.pointee.all_workspaces):
+ DispatchQueue.main.async {
+ self.window?.setOnAllWorkspaces(Bool(mpv.opts.all_workspaces))
+ }
+ case MPVHelper.getPointer(&mpv.optsPtr.pointee.keepaspect_window):
DispatchQueue.main.async {
self.window?.keepAspect = Bool(mpv.opts.keepaspect_window)
}
- }
- if opt == UnsafeMutableRawPointer(&mpv.optsPtr.pointee.window_minimized) {
+ case MPVHelper.getPointer(&mpv.optsPtr.pointee.window_minimized):
DispatchQueue.main.async {
self.window?.setMinimized(Bool(mpv.opts.window_minimized))
}
- }
- if opt == UnsafeMutableRawPointer(&mpv.optsPtr.pointee.window_maximized) {
+ case MPVHelper.getPointer(&mpv.optsPtr.pointee.window_maximized):
DispatchQueue.main.async {
self.window?.setMaximized(Bool(mpv.opts.window_maximized))
}
+ default:
+ break
}
}
return VO_TRUE
@@ -653,19 +675,14 @@ class Common: NSObject {
return
}
- var o: UnsafeMutableRawPointer?
- while mpv.nextChangedMacOption(property: &o) {
- guard let opt = o else {
- log.sendWarning("Could not retrieve changed mac option")
- return
- }
-
+ var opt: UnsafeMutableRawPointer?
+ while mpv.nextChangedMacOption(property: &opt) {
switch opt {
- case UnsafeMutableRawPointer(&mpv.macOptsPtr.pointee.macos_title_bar_appearance):
+ case MPVHelper.getPointer(&mpv.macOptsPtr.pointee.macos_title_bar_appearance):
titleBar?.set(appearance: Int(mpv.macOpts.macos_title_bar_appearance))
- case UnsafeMutableRawPointer(&mpv.macOptsPtr.pointee.macos_title_bar_material):
+ case MPVHelper.getPointer(&mpv.macOptsPtr.pointee.macos_title_bar_material):
titleBar?.set(material: Int(mpv.macOpts.macos_title_bar_material))
- case UnsafeMutableRawPointer(&mpv.macOptsPtr.pointee.macos_title_bar_color):
+ case MPVHelper.getPointer(&mpv.macOptsPtr.pointee.macos_title_bar_color):
titleBar?.set(color: mpv.macOpts.macos_title_bar_color)
default:
break
diff --git a/video/out/mac/title_bar.swift b/video/out/mac/title_bar.swift
index 623fe8f57d..e49c2bb99c 100644
--- a/video/out/mac/title_bar.swift
+++ b/video/out/mac/title_bar.swift
@@ -151,7 +151,7 @@ class TitleBar: NSVisualEffectView {
}
}
- @objc func hide() {
+ @objc func hide(_ duration: TimeInterval = 0.20) {
guard let window = common.window else { return }
if window.isInFullscreen && !window.isAnimating {
alphaValue = 0
@@ -159,7 +159,7 @@ class TitleBar: NSVisualEffectView {
return
}
NSAnimationContext.runAnimationGroup({ (context) -> Void in
- context.duration = 0.20
+ context.duration = duration
systemBar?.animator().alphaValue = 0
animator().alphaValue = 0
}, completionHandler: {
diff --git a/video/out/mac/window.swift b/video/out/mac/window.swift
index d692d0db91..8b6c5f0518 100644
--- a/video/out/mac/window.swift
+++ b/video/out/mac/window.swift
@@ -30,7 +30,7 @@ class Window: NSWindow, NSWindowDelegate {
var isInFullscreen: Bool = false
var isAnimating: Bool = false
var isMoving: Bool = false
- var forceTargetScreen: Bool = false
+ var previousStyleMask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable]
var unfsContentFramePixel: NSRect { get { return convertToBacking(unfsContentFrame ?? NSRect(x: 0, y: 0, width: 160, height: 90)) } }
var framePixel: NSRect { get { return convertToBacking(frame) } }
@@ -61,6 +61,7 @@ class Window: NSWindow, NSWindowDelegate {
set {
let responder = firstResponder
let windowTitle = title
+ previousStyleMask = super.styleMask
super.styleMask = newValue
makeFirstResponder(responder)
title = windowTitle
@@ -163,22 +164,26 @@ class Window: NSWindow, NSWindowDelegate {
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = getFsAnimationDuration(duration - 0.05)
window.animator().setFrame(tScreen.frame, display: true)
- }, completionHandler: { })
+ }, completionHandler: nil)
}
func window(_ window: NSWindow, startCustomAnimationToExitFullScreenWithDuration duration: TimeInterval) {
guard let tScreen = targetScreen, let currentScreen = screen else { return }
let newFrame = calculateWindowPosition(for: tScreen, withoutBounds: tScreen == screen)
let intermediateFrame = aspectFit(rect: newFrame, in: currentScreen.frame)
- common.view?.layerContentsPlacement = .scaleProportionallyToFill
- common.titleBar?.hide()
- styleMask.remove(.fullScreen)
- setFrame(intermediateFrame, display: true)
+ common.titleBar?.hide(0.0)
NSAnimationContext.runAnimationGroup({ (context) -> Void in
- context.duration = getFsAnimationDuration(duration - 0.05)
- window.animator().setFrame(newFrame, display: true)
- }, completionHandler: { })
+ context.duration = 0.0
+ common.view?.layerContentsPlacement = .scaleProportionallyToFill
+ window.animator().setFrame(intermediateFrame, display: true)
+ }, completionHandler: {
+ NSAnimationContext.runAnimationGroup({ (context) -> Void in
+ context.duration = self.getFsAnimationDuration(duration - 0.05)
+ self.styleMask.remove(.fullScreen)
+ window.animator().setFrame(newFrame, display: true)
+ }, completionHandler: nil)
+ })
}
func windowDidEnterFullScreen(_ notification: Notification) {
@@ -225,7 +230,14 @@ class Window: NSWindow, NSWindowDelegate {
func setToFullScreen() {
guard let targetFrame = targetScreen?.frame else { return }
- styleMask.insert(.fullScreen)
+
+ if #available(macOS 11.0, *) {
+ styleMask = .borderless
+ common.titleBar?.hide(0.0)
+ } else {
+ styleMask.insert(.fullScreen)
+ }
+
NSApp.presentationOptions = [.autoHideMenuBar, .autoHideDock]
setFrame(targetFrame, display: true)
endAnimation()
@@ -236,10 +248,17 @@ class Window: NSWindow, NSWindowDelegate {
func setToWindow() {
guard let tScreen = targetScreen else { return }
+
+ if #available(macOS 11.0, *) {
+ styleMask = previousStyleMask
+ common.titleBar?.hide(0.0)
+ } else {
+ styleMask.remove(.fullScreen)
+ }
+
let newFrame = calculateWindowPosition(for: tScreen, withoutBounds: targetScreen == screen)
NSApp.presentationOptions = []
setFrame(newFrame, display: true)
- styleMask.remove(.fullScreen)
endAnimation()
isInFullscreen = false
mpv?.setOption(fullscreen: isInFullscreen)
@@ -274,6 +293,14 @@ class Window: NSWindow, NSWindowDelegate {
}
}
+ func setOnAllWorkspaces(_ state: Bool) {
+ if state {
+ collectionBehavior.insert(.canJoinAllSpaces)
+ } else {
+ collectionBehavior.remove(.canJoinAllSpaces)
+ }
+ }
+
func setMinimized(_ stateWanted: Bool) {
if isMiniaturized == stateWanted { return }
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index 9b50dda702..05c2b6c436 100644
--- a/video/out/opengl/context_wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -39,79 +39,6 @@ struct priv {
struct wl_egl_window *egl_window;
};
-static const struct wp_presentation_feedback_listener feedback_listener;
-
-static void feedback_sync_output(void *data, struct wp_presentation_feedback *fback,
- struct wl_output *output)
-{
-}
-
-static void feedback_presented(void *data, struct wp_presentation_feedback *fback,
- uint32_t tv_sec_hi, uint32_t tv_sec_lo,
- uint32_t tv_nsec, uint32_t refresh_nsec,
- uint32_t seq_hi, uint32_t seq_lo,
- uint32_t flags)
-{
- struct vo_wayland_state *wl = data;
- vo_wayland_sync_shift(wl);
-
- if (fback)
- wp_presentation_feedback_destroy(fback);
-
- // Very similar to oml_sync_control, in this case we assume that every
- // time the compositor receives feedback, a buffer swap has been already
- // been performed.
- //
- // Notes:
- // - tv_sec_lo + tv_sec_hi is the equivalent of oml's ust
- // - seq_lo + seq_hi is the equivalent of oml's msc
- // - these values are updated everytime the compositor receives feedback.
-
- int index = last_available_sync(wl);
- if (index < 0) {
- queue_new_sync(wl);
- index = 0;
- }
- int64_t sec = (uint64_t) tv_sec_lo + ((uint64_t) tv_sec_hi << 32);
- wl->sync[index].ust = sec * 1000000LL + (uint64_t) tv_nsec / 1000;
- wl->sync[index].msc = (uint64_t) seq_lo + ((uint64_t) seq_hi << 32);
- wl->sync[index].filled = true;
-}
-
-static void feedback_discarded(void *data, struct wp_presentation_feedback *fback)
-{
-}
-
-static const struct wp_presentation_feedback_listener feedback_listener = {
- feedback_sync_output,
- feedback_presented,
- feedback_discarded,
-};
-
-static const struct wl_callback_listener frame_listener;
-
-static void frame_callback(void *data, struct wl_callback *callback, uint32_t time)
-{
- struct vo_wayland_state *wl = data;
-
- if (callback)
- wl_callback_destroy(callback);
-
- wl->frame_callback = wl_surface_frame(wl->surface);
- wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
-
- if (wl->presentation) {
- wl->feedback = wp_presentation_feedback(wl->presentation, wl->surface);
- wp_presentation_feedback_add_listener(wl->feedback, &feedback_listener, wl);
- }
-
- wl->frame_wait = false;
-}
-
-static const struct wl_callback_listener frame_listener = {
- frame_callback,
-};
-
static void resize(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
@@ -209,9 +136,6 @@ static bool egl_create_context(struct ra_ctx *ctx)
ra_add_native_resource(ctx->ra, "wl", wl->display);
- wl->frame_callback = wl_surface_frame(wl->surface);
- wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
-
return true;
}
diff --git a/video/out/placebo/ra_pl.c b/video/out/placebo/ra_pl.c
index f8df590511..d8e8729ed3 100644
--- a/video/out/placebo/ra_pl.c
+++ b/video/out/placebo/ra_pl.c
@@ -22,9 +22,7 @@ const struct pl_gpu *ra_pl_get(const struct ra *ra)
return ra->fns == &ra_fns_pl ? get_gpu(ra) : NULL;
}
-#if PL_API_VER >= 60
static struct pl_timer *get_active_timer(const struct ra *ra);
-#endif
struct ra *ra_create_pl(const struct pl_gpu *gpu, struct mp_log *log)
{
@@ -144,8 +142,14 @@ bool mppl_wrap_tex(struct ra *ra, const struct pl_tex *pltex,
.blit_dst = pltex->params.blit_dst,
.host_mutable = pltex->params.host_writable,
.downloadable = pltex->params.host_readable,
+#if PL_API_VER >= 103
+ // These don't exist upstream, so just pick something reasonable
+ .src_linear = pltex->params.format->caps & PL_FMT_CAP_LINEAR,
+ .src_repeat = false,
+#else
.src_linear = pltex->params.sample_mode == PL_TEX_SAMPLE_LINEAR,
.src_repeat = pltex->params.address_mode == PL_TEX_ADDRESS_REPEAT,
+#endif
},
.priv = (void *) pltex,
};
@@ -157,32 +161,6 @@ static struct ra_tex *tex_create_pl(struct ra *ra,
const struct ra_tex_params *params)
{
const struct pl_gpu *gpu = get_gpu(ra);
-
- // Check size limits
- bool ok = false;
- switch (params->dimensions) {
- case 1:
- ok = params->w <= gpu->limits.max_tex_1d_dim;
- break;
-
- case 2:
- ok = params->w <= gpu->limits.max_tex_2d_dim &&
- params->h <= gpu->limits.max_tex_2d_dim;
- break;
-
- case 3:
- ok = params->w <= gpu->limits.max_tex_2d_dim &&
- params->h <= gpu->limits.max_tex_2d_dim &&
- params->d <= gpu->limits.max_tex_2d_dim;
- break;
- };
-
- if (!ok) {
- MP_ERR(ra, "Texture size %dx%dx%d exceeds dimension limits!\n",
- params->w, params->h, params->d);
- return NULL;
- }
-
const struct pl_tex *pltex = pl_tex_create(gpu, &(struct pl_tex_params) {
.w = params->w,
.h = params->dimensions >= 2 ? params->h : 0,
@@ -195,10 +173,12 @@ static struct ra_tex *tex_create_pl(struct ra *ra,
.blit_dst = params->blit_dst || params->render_dst,
.host_writable = params->host_mutable,
.host_readable = params->downloadable,
+#if PL_API_VER < 103
.sample_mode = params->src_linear ? PL_TEX_SAMPLE_LINEAR
: PL_TEX_SAMPLE_NEAREST,
.address_mode = params->src_repeat ? PL_TEX_ADDRESS_REPEAT
: PL_TEX_ADDRESS_CLAMP,
+#endif
.initial_data = params->initial_data,
});
@@ -230,9 +210,7 @@ static bool tex_upload_pl(struct ra *ra, const struct ra_tex_upload_params *para
.buf = params->buf ? params->buf->priv : NULL,
.buf_offset = params->buf_offset,
.ptr = (void *) params->src,
-#if PL_API_VER >= 60
.timer = get_active_timer(ra),
-#endif
};
const struct pl_buf *staging = NULL;
@@ -285,9 +263,7 @@ static bool tex_download_pl(struct ra *ra, struct ra_tex_download_params *params
.tex = tex,
.ptr = params->dst,
.stride_w = params->stride / texel_size,
-#if PL_API_VER >= 60
.timer = get_active_timer(ra),
-#endif
};
uint8_t *staging = NULL;
@@ -320,19 +296,7 @@ static struct ra_buf *buf_create_pl(struct ra *ra,
[RA_BUF_TYPE_SHARED_MEMORY] = 0,
};
- const struct pl_gpu *gpu = get_gpu(ra);
- size_t max_size[] = {
- [PL_BUF_TEX_TRANSFER] = gpu->limits.max_xfer_size,
- [PL_BUF_UNIFORM] = gpu->limits.max_ubo_size,
- [PL_BUF_STORAGE] = gpu->limits.max_ssbo_size,
- };
-
- if (params->size > max_size[buf_type[params->type]]) {
- MP_ERR(ra, "Buffer size %zu exceeds size limits!\n", params->size);
- return NULL;
- }
-
- const struct pl_buf *plbuf = pl_buf_create(gpu, &(struct pl_buf_params) {
+ const struct pl_buf *plbuf = pl_buf_create(get_gpu(ra), &(struct pl_buf_params) {
.type = buf_type[params->type],
.size = params->size,
.host_mapped = params->host_mapped,
@@ -399,7 +363,18 @@ static void blit_pl(struct ra *ra, struct ra_tex *dst, struct ra_tex *src,
pldst.y1 = MPMIN(MPMAX(dst_rc->y1, 0), dst->params.h);
}
+#if PL_API_VER >= 103
+ pl_tex_blit(get_gpu(ra), &(struct pl_tex_blit_params) {
+ .src = src->priv,
+ .dst = dst->priv,
+ .src_rc = plsrc,
+ .dst_rc = pldst,
+ .sample_mode = src->params.src_linear ? PL_TEX_SAMPLE_LINEAR
+ : PL_TEX_SAMPLE_NEAREST,
+ });
+#else
pl_tex_blit(get_gpu(ra), dst->priv, src->priv, pldst, plsrc);
+#endif
}
static const enum pl_var_type var_type[RA_VARTYPE_COUNT] = {
@@ -627,9 +602,17 @@ static void renderpass_run_pl(struct ra *ra,
struct pl_desc_binding bind;
switch (inp->type) {
case RA_VARTYPE_TEX:
- case RA_VARTYPE_IMG_W:
- bind.object = (* (struct ra_tex **) val->data)->priv;
+ case RA_VARTYPE_IMG_W: {
+ struct ra_tex *tex = *((struct ra_tex **) val->data);
+ bind.object = tex->priv;
+#if PL_API_VER >= 103
+ bind.sample_mode = tex->params.src_linear ? PL_TEX_SAMPLE_LINEAR
+ : PL_TEX_SAMPLE_NEAREST;
+ bind.address_mode = tex->params.src_repeat ? PL_TEX_ADDRESS_REPEAT
+ : PL_TEX_ADDRESS_CLAMP;
+#endif
break;
+ }
case RA_VARTYPE_BUF_RO:
case RA_VARTYPE_BUF_RW:
bind.object = (* (struct ra_buf **) val->data)->priv;
@@ -666,8 +649,6 @@ static void renderpass_run_pl(struct ra *ra,
pl_pass_run(get_gpu(ra), &pl_params);
}
-#if PL_API_VER >= 60
-
struct ra_timer_pl {
// Because libpplacebo only supports one operation per timer, we need
// to use multiple pl_timers to sum up multiple passes/transfers
@@ -739,8 +720,6 @@ static struct pl_timer *get_active_timer(const struct ra *ra)
return t->timers[t->idx_timers++];
}
-#endif // PL_API_VER >= 60
-
static struct ra_fns ra_fns_pl = {
.destroy = destroy_ra_pl,
.tex_create = tex_create_pl,
@@ -759,11 +738,9 @@ static struct ra_fns ra_fns_pl = {
.renderpass_create = renderpass_create_pl,
.renderpass_destroy = renderpass_destroy_pl,
.renderpass_run = renderpass_run_pl,
-#if PL_API_VER >= 60
.timer_create = timer_create_pl,
.timer_destroy = timer_destroy_pl,
.timer_start = timer_start_pl,
.timer_stop = timer_stop_pl,
-#endif
};
diff --git a/video/out/vo_sixel.c b/video/out/vo_sixel.c
index 8318c5190c..c9cc1573cb 100644
--- a/video/out/vo_sixel.c
+++ b/video/out/vo_sixel.c
@@ -63,19 +63,19 @@ struct priv {
int opt_pad_x;
int opt_rows;
int opt_cols;
+ int opt_clear;
// Internal data
sixel_output_t *output;
sixel_dither_t *dither;
sixel_dither_t *testdither;
uint8_t *buffer;
+ bool skip_frame_draw;
- // The dimensions that will be actually
- // be used after processing user inputs
- int top;
- int left;
- int width;
- int height;
+ int left, top; // image origin cell (1 based)
+ int width, height; // actual image px size - always reflects dst_rect.
+ int num_cols, num_rows; // terminal size in cells
+ int canvas_ok; // whether canvas vo->dwidth and vo->dheight are positive
int previous_histgram_colors;
@@ -115,7 +115,7 @@ static int detect_scene_change(struct vo* vo)
}
-static void dealloc_dithers_and_buffer(struct vo* vo)
+static void dealloc_dithers_and_buffers(struct vo* vo)
{
struct priv* priv = vo->priv;
@@ -124,6 +124,11 @@ static void dealloc_dithers_and_buffer(struct vo* vo)
priv->buffer = NULL;
}
+ if (priv->frame) {
+ talloc_free(priv->frame);
+ priv->frame = NULL;
+ }
+
if (priv->dither) {
sixel_dither_unref(priv->dither);
priv->dither = NULL;
@@ -139,15 +144,15 @@ static SIXELSTATUS prepare_static_palette(struct vo* vo)
{
struct priv* priv = vo->priv;
- if (priv->dither) {
- sixel_dither_set_body_only(priv->dither, 1);
- } else {
+ if (!priv->dither) {
priv->dither = sixel_dither_get(BUILTIN_XTERM256);
if (priv->dither == NULL)
return SIXEL_FALSE;
sixel_dither_set_diffusion_type(priv->dither, priv->opt_diffuse);
}
+
+ sixel_dither_set_body_only(priv->dither, 0);
return SIXEL_OK;
}
@@ -159,7 +164,8 @@ static SIXELSTATUS prepare_dynamic_palette(struct vo *vo)
/* create histgram and construct color palette
* with median cut algorithm. */
status = sixel_dither_initialize(priv->testdither, priv->buffer,
- priv->width, priv->height, 3,
+ priv->width, priv->height,
+ SIXEL_PIXELFORMAT_RGB888,
LARGE_NORM, REP_CENTER_BOX,
QUALITY_LOW);
if (SIXEL_FAILED(status))
@@ -179,22 +185,18 @@ static SIXELSTATUS prepare_dynamic_palette(struct vo *vo)
sixel_dither_set_diffusion_type(priv->dither, priv->opt_diffuse);
} else {
- if (priv->dither == NULL) {
+ if (priv->dither == NULL)
return SIXEL_FALSE;
- }
- sixel_dither_set_body_only(priv->dither, 1);
}
+ sixel_dither_set_body_only(priv->dither, 0);
return status;
}
-static void resize(struct vo *vo)
+static void update_canvas_dimensions(struct vo *vo)
{
// this function sets the vo canvas size in pixels vo->dwidth, vo->dheight,
- // and the output scaled size in priv->width, priv->height
- // and the scaling rectangles in pixels priv->src_rect, priv->dst_rect
- // as well as image positioning in cells priv->top, priv->left.
- // no other scaling/rendering size values are required past this point.
+ // and the number of rows and columns available in priv->num_rows/cols
struct priv *priv = vo->priv;
int num_rows = TERMINAL_FALLBACK_ROWS;
int num_cols = TERMINAL_FALLBACK_COLS;
@@ -254,6 +256,19 @@ static void resize(struct vo *vo)
vo->dheight = total_px_height * (num_rows - 1) / num_rows / 6 * 6;
vo->dwidth = total_px_width;
+ priv->num_rows = num_rows;
+ priv->num_cols = num_cols;
+
+ priv->canvas_ok = vo->dwidth > 0 && vo->dheight > 0;
+}
+
+static void set_sixel_output_parameters(struct vo *vo)
+{
+ // This function sets output scaled size in priv->width, priv->height
+ // and the scaling rectangles in pixels priv->src_rect, priv->dst_rect
+ // as well as image positioning in cells priv->top, priv->left.
+ struct priv *priv = vo->priv;
+
vo_get_src_dst_rects(vo, &priv->src_rect, &priv->dst_rect, &priv->osd);
// priv->width and priv->height are the width and height of dst_rect
@@ -265,15 +280,14 @@ static void resize(struct vo *vo)
// top/left values must be greater than 1. If it is set, then
// the image will be rendered from there and no further centering is done.
priv->top = (priv->opt_top > 0) ? priv->opt_top :
- num_rows * priv->dst_rect.y0 / vo->dheight + 1;
+ priv->num_rows * priv->dst_rect.y0 / vo->dheight + 1;
priv->left = (priv->opt_left > 0) ? priv->opt_left :
- num_cols * priv->dst_rect.x0 / vo->dwidth + 1;
+ priv->num_cols * priv->dst_rect.x0 / vo->dwidth + 1;
}
-static int reconfig(struct vo *vo, struct mp_image_params *params)
+static int update_sixel_swscaler(struct vo *vo, struct mp_image_params *params)
{
struct priv *priv = vo->priv;
- resize(vo);
priv->sws->src = *params;
priv->sws->src.w = mp_rect_w(priv->src_rect);
@@ -286,6 +300,8 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
.p_h = 1,
};
+ dealloc_dithers_and_buffers(vo);
+
priv->frame = mp_image_alloc(IMGFMT, priv->width, priv->height);
if (!priv->frame)
return -1;
@@ -293,17 +309,15 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
if (mp_sws_reinit(priv->sws) < 0)
return -1;
- printf(ESC_HIDE_CURSOR);
- printf(ESC_CLEAR_SCREEN);
- vo->want_redraw = true;
-
- dealloc_dithers_and_buffer(vo);
- SIXELSTATUS status = sixel_dither_new(&priv->testdither,
- priv->opt_reqcolors, NULL);
- if (SIXEL_FAILED(status)) {
- MP_ERR(vo, "reconfig: Failed to create new dither: %s\n",
- sixel_helper_format_error(status));
- return -1;
+ // create testdither only if dynamic palette mode is set
+ if (!priv->opt_fixedpal) {
+ SIXELSTATUS status = sixel_dither_new(&priv->testdither,
+ priv->opt_reqcolors, NULL);
+ if (SIXEL_FAILED(status)) {
+ MP_ERR(vo, "update_sixel_swscaler: Failed to create new dither: %s\n",
+ sixel_helper_format_error(status));
+ return -1;
+ }
}
priv->buffer =
@@ -312,28 +326,82 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
return 0;
}
-static void draw_image(struct vo *vo, mp_image_t *mpi)
+static int reconfig(struct vo *vo, struct mp_image_params *params)
+{
+ struct priv *priv = vo->priv;
+ int ret = 0;
+ update_canvas_dimensions(vo);
+ if (priv->canvas_ok) { // if too small - succeed but skip the rendering
+ set_sixel_output_parameters(vo);
+ ret = update_sixel_swscaler(vo, params);
+ }
+
+ printf(ESC_CLEAR_SCREEN);
+ vo->want_redraw = true;
+
+ return ret;
+}
+
+static void draw_frame(struct vo *vo, struct vo_frame *frame)
{
struct priv *priv = vo->priv;
- struct mp_image src = *mpi;
SIXELSTATUS status;
+ struct mp_image *mpi = NULL;
+
+ int prev_rows = priv->num_rows;
+ int prev_cols = priv->num_cols;
+ int prev_height = vo->dheight;
+ int prev_width = vo->dwidth;
+ bool resized = false;
+ update_canvas_dimensions(vo);
+ if (!priv->canvas_ok)
+ return;
- struct mp_rect src_rc = priv->src_rect;
- src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);
- src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y);
- mp_image_crop_rc(&src, src_rc);
+ if (prev_rows != priv->num_rows || prev_cols != priv->num_cols ||
+ prev_width != vo->dwidth || prev_height != vo->dheight)
+ {
+ set_sixel_output_parameters(vo);
+ // Not checking for vo->config_ok because draw_frame is never called
+ // with a failed reconfig.
+ update_sixel_swscaler(vo, vo->params);
- // Downscale the image
- mp_sws_scale(priv->sws, priv->frame, &src);
+ printf(ESC_CLEAR_SCREEN);
+ resized = true;
+ }
+
+ if (frame->repeat && !frame->redraw && !resized) {
+ // Frame is repeated, and no need to update OSD either
+ priv->skip_frame_draw = true;
+ return;
+ } else {
+ // Either frame is new, or OSD has to be redrawn
+ priv->skip_frame_draw = false;
+ }
+
+ // Normal case where we have to draw the frame and the image is not NULL
+ if (frame->current) {
+ mpi = mp_image_new_ref(frame->current);
+ struct mp_rect src_rc = priv->src_rect;
+ src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);
+ src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y);
+ mp_image_crop_rc(mpi, src_rc);
+
+ // scale/pan to our dest rect
+ mp_sws_scale(priv->sws, priv->frame, mpi);
+ } else {
+ // Image is NULL, so need to clear image and draw OSD
+ mp_image_clear(priv->frame, 0, 0, priv->width, priv->height);
+ }
struct mp_osd_res dim = {
.w = priv->width,
.h = priv->height
};
osd_draw_on_image(vo->osd, dim, mpi ? mpi->pts : 0, 0, priv->frame);
+
// Copy from mpv to RGB format as required by libsixel
- memcpy_pic(priv->buffer, priv->frame->planes[0], priv->width * depth, priv->height,
- priv->width * depth, priv->frame->stride[0]);
+ memcpy_pic(priv->buffer, priv->frame->planes[0], priv->width * depth,
+ priv->height, priv->width * depth, priv->frame->stride[0]);
// Even if either of these prepare palette functions fail, on re-running them
// they should try to re-initialize the dithers, so it shouldn't dereference
@@ -346,11 +414,12 @@ static void draw_image(struct vo *vo, mp_image_t *mpi)
}
if (SIXEL_FAILED(status)) {
- MP_WARN(vo, "draw_image: prepare_palette returned error: %s\n",
+ MP_WARN(vo, "draw_frame: prepare_palette returned error: %s\n",
sixel_helper_format_error(status));
}
- talloc_free(mpi);
+ if (mpi)
+ talloc_free(mpi);
}
static int sixel_write(char *data, int size, void *priv)
@@ -361,6 +430,12 @@ static int sixel_write(char *data, int size, void *priv)
static void flip_page(struct vo *vo)
{
struct priv* priv = vo->priv;
+ if (!priv->canvas_ok)
+ return;
+
+ // If frame is repeated and no update required, then we skip encoding
+ if (priv->skip_frame_draw)
+ return;
// Make sure that image and dither are valid before drawing
if (priv->buffer == NULL || priv->dither == NULL)
@@ -369,8 +444,7 @@ static void flip_page(struct vo *vo)
// Go to the offset row and column, then display the image
printf(ESC_GOTOXY, priv->top, priv->left);
sixel_encode(priv->buffer, priv->width, priv->height,
- PIXELFORMAT_RGB888,
- priv->dither, priv->output);
+ depth, priv->dither, priv->output);
fflush(stdout);
}
@@ -400,18 +474,17 @@ static int preinit(struct vo *vo)
printf(ESC_USE_GLOBAL_COLOR_REG);
priv->dither = NULL;
- status = sixel_dither_new(&priv->testdither, priv->opt_reqcolors, NULL);
- if (SIXEL_FAILED(status)) {
- MP_ERR(vo, "preinit: Failed to create new dither: %s\n",
- sixel_helper_format_error(status));
- return -1;
+ // create testdither only if dynamic palette mode is set
+ if (!priv->opt_fixedpal) {
+ status = sixel_dither_new(&priv->testdither, priv->opt_reqcolors, NULL);
+ if (SIXEL_FAILED(status)) {
+ MP_ERR(vo, "preinit: Failed to create new dither: %s\n",
+ sixel_helper_format_error(status));
+ return -1;
+ }
}
- resize(vo);
- priv->buffer =
- talloc_array(NULL, uint8_t, depth * priv->width * priv->height);
-
priv->previous_histgram_colors = 0;
return 0;
@@ -424,15 +497,9 @@ static int query_format(struct vo *vo, int format)
static int control(struct vo *vo, uint32_t request, void *data)
{
- if (request == VOCTRL_SET_PANSCAN) {
- if (!reconfig(vo, vo->params)) {
- return VO_TRUE;
- } else {
- return VO_FALSE;
- }
- } else {
- return VO_NOTIMPL;
- }
+ if (request == VOCTRL_SET_PANSCAN)
+ return (vo->config_ok && !reconfig(vo, vo->params)) ? VO_TRUE : VO_FALSE;
+ return VO_NOTIMPL;
}
@@ -442,8 +509,10 @@ static void uninit(struct vo *vo)
printf(ESC_RESTORE_CURSOR);
- printf(ESC_CLEAR_SCREEN);
- printf(ESC_GOTOXY, 1, 1);
+ if (priv->opt_clear) {
+ printf(ESC_CLEAR_SCREEN);
+ printf(ESC_GOTOXY, 1, 1);
+ }
fflush(stdout);
if (priv->output) {
@@ -451,24 +520,24 @@ static void uninit(struct vo *vo)
priv->output = NULL;
}
- dealloc_dithers_and_buffer(vo);
+ dealloc_dithers_and_buffers(vo);
}
#define OPT_BASE_STRUCT struct priv
const struct vo_driver video_out_sixel = {
.name = "sixel",
- .description = "libsixel",
+ .description = "terminal graphics using sixels",
.preinit = preinit,
.query_format = query_format,
.reconfig = reconfig,
.control = control,
- .draw_image = draw_image,
+ .draw_frame = draw_frame,
.flip_page = flip_page,
.uninit = uninit,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
- .opt_diffuse = DIFFUSE_ATKINSON,
+ .opt_diffuse = DIFFUSE_AUTO,
.opt_width = 0,
.opt_height = 0,
.opt_reqcolors = 256,
@@ -480,6 +549,7 @@ const struct vo_driver video_out_sixel = {
.opt_pad_x = -1,
.opt_rows = 0,
.opt_cols = 0,
+ .opt_clear = 1,
},
.options = (const m_option_t[]) {
{"dither", OPT_CHOICE(opt_diffuse,
@@ -503,6 +573,7 @@ const struct vo_driver video_out_sixel = {
{"pad-x", OPT_INT(opt_pad_x)},
{"rows", OPT_INT(opt_rows)},
{"cols", OPT_INT(opt_cols)},
+ {"exit-clear", OPT_FLAG(opt_clear), },
{0}
},
.options_prefix = "vo-sixel",
diff --git a/video/out/vo_tct.c b/video/out/vo_tct.c
index b0965e14cc..9325d3cbfa 100644
--- a/video/out/vo_tct.c
+++ b/video/out/vo_tct.c
@@ -76,7 +76,6 @@ static const struct m_sub_options vo_tct_conf = {
struct priv {
struct vo_tct_opts *opts;
size_t buffer_size;
- char *buffer;
int swidth;
int sheight;
struct mp_image *frame;
@@ -200,9 +199,6 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
p->swidth = p->dst.x1 - p->dst.x0;
p->sheight = p->dst.y1 - p->dst.y0;
- if (p->buffer)
- free(p->buffer);
-
p->sws->src = *params;
p->sws->dst = (struct mp_image_params) {
.imgfmt = IMGFMT,
@@ -213,6 +209,8 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
};
const int mul = (p->opts->algo == ALGO_PLAIN ? 1 : 2);
+ if (p->frame)
+ talloc_free(p->frame);
p->frame = mp_image_alloc(IMGFMT, p->swidth, p->sheight * mul);
if (!p->frame)
return -1;
@@ -258,8 +256,8 @@ static void uninit(struct vo *vo)
printf(ESC_CLEAR_SCREEN);
printf(ESC_GOTOXY, 0, 0);
struct priv *p = vo->priv;
- if (p->buffer)
- talloc_free(p->buffer);
+ if (p->frame)
+ talloc_free(p->frame);
}
static int preinit(struct vo *vo)
diff --git a/video/out/vo_wlshm.c b/video/out/vo_wlshm.c
index f6c37714da..a71f9507fc 100644
--- a/video/out/vo_wlshm.c
+++ b/video/out/vo_wlshm.c
@@ -23,6 +23,7 @@
#include <libswscale/swscale.h>
+#include "osdep/endian.h"
#include "sub/osd.h"
#include "video/fmt-conversion.h"
#include "video/mp_image.h"
@@ -73,25 +74,6 @@ static void buffer_destroy(void *p)
munmap(buf->mpi.planes[0], buf->size);
}
-static const struct wl_callback_listener frame_listener;
-
-static void frame_callback(void *data, struct wl_callback *callback, uint32_t time)
-{
- struct vo_wayland_state *wl = data;
-
- if (callback)
- wl_callback_destroy(callback);
-
- wl->frame_callback = wl_surface_frame(wl->surface);
- wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
-
- wl->frame_wait = false;
-}
-
-static const struct wl_callback_listener frame_listener = {
- frame_callback,
-};
-
static int allocate_memfd(size_t size)
{
int fd = memfd_create("mpv", MFD_CLOEXEC | MFD_ALLOW_SEALING);
@@ -142,10 +124,6 @@ static struct buffer *buffer_create(struct vo *vo, int width, int height)
if (!buf->buffer)
goto error4;
wl_buffer_add_listener(buf->buffer, &buffer_listener, buf);
- if (!wl->frame_callback) {
- wl->frame_callback = wl_surface_frame(wl->surface);
- wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
- }
close(fd);
talloc_set_destructor(buf, buffer_destroy);
@@ -207,7 +185,7 @@ static int resize(struct vo *vo)
vo->dheight = height;
vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);
p->sws->dst = (struct mp_image_params) {
- .imgfmt = IMGFMT_BGR0,
+ .imgfmt = MP_SELECT_LE_BE(IMGFMT_BGR0, IMGFMT_0RGB),
.w = width,
.h = height,
.p_w = 1,
@@ -298,6 +276,19 @@ static void flip_page(struct vo *vo)
if (!wl->opts->disable_vsync)
vo_wayland_wait_frame(wl);
+
+ if (wl->presentation)
+ wayland_sync_swap(wl);
+}
+
+static void get_vsync(struct vo *vo, struct vo_vsync_info *info)
+{
+ struct vo_wayland_state *wl = vo->wl;
+ if (wl->presentation) {
+ info->vsync_duration = wl->vsync_duration;
+ info->skipped_vsyncs = wl->last_skipped_vsyncs;
+ info->last_queue_display_time = wl->last_queue_display_time;
+ }
}
static void uninit(struct vo *vo)
@@ -327,6 +318,7 @@ const struct vo_driver video_out_wlshm = {
.control = control,
.draw_image = draw_image,
.flip_page = flip_page,
+ .get_vsync = get_vsync,
.wakeup = vo_wayland_wakeup,
.wait_events = vo_wayland_wait_events,
.uninit = uninit,
diff --git a/video/out/vulkan/context_wayland.c b/video/out/vulkan/context_wayland.c
index 98bc8afcfc..8782af5527 100644
--- a/video/out/vulkan/context_wayland.c
+++ b/video/out/vulkan/context_wayland.c
@@ -29,79 +29,6 @@ struct priv {
struct mpvk_ctx vk;
};
-static const struct wp_presentation_feedback_listener feedback_listener;
-
-static void feedback_sync_output(void *data, struct wp_presentation_feedback *fback,
- struct wl_output *output)
-{
-}
-
-static void feedback_presented(void *data, struct wp_presentation_feedback *fback,
- uint32_t tv_sec_hi, uint32_t tv_sec_lo,
- uint32_t tv_nsec, uint32_t refresh_nsec,
- uint32_t seq_hi, uint32_t seq_lo,
- uint32_t flags)
-{
- struct vo_wayland_state *wl = data;
- vo_wayland_sync_shift(wl);
-
- if (fback)
- wp_presentation_feedback_destroy(fback);
-
- // Very similar to oml_sync_control, in this case we assume that every
- // time the compositor receives feedback, a buffer swap has been already
- // been performed.
- //
- // Notes:
- // - tv_sec_lo + tv_sec_hi is the equivalent of oml's ust
- // - seq_lo + seq_hi is the equivalent of oml's msc
- // - these values are updated everytime the compositor receives feedback.
-
- int index = last_available_sync(wl);
- if (index < 0) {
- queue_new_sync(wl);
- index = 0;
- }
- int64_t sec = (uint64_t) tv_sec_lo + ((uint64_t) tv_sec_hi << 32);
- wl->sync[index].ust = sec * 1000000LL + (uint64_t) tv_nsec / 1000;
- wl->sync[index].msc = (uint64_t) seq_lo + ((uint64_t) seq_hi << 32);
- wl->sync[index].filled = true;
-}
-
-static void feedback_discarded(void *data, struct wp_presentation_feedback *fback)
-{
-}
-
-static const struct wp_presentation_feedback_listener feedback_listener = {
- feedback_sync_output,
- feedback_presented,
- feedback_discarded,
-};
-
-static const struct wl_callback_listener frame_listener;
-
-static void frame_callback(void *data, struct wl_callback *callback, uint32_t time)
-{
- struct vo_wayland_state *wl = data;
-
- if (callback)
- wl_callback_destroy(callback);
-
- wl->frame_callback = wl_surface_frame(wl->surface);
- wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
-
- if (wl->presentation) {
- wl->feedback = wp_presentation_feedback(wl->presentation, wl->surface);
- wp_presentation_feedback_add_listener(wl->feedback, &feedback_listener, wl);
- }
-
- wl->frame_wait = false;
-}
-
-static const struct wl_callback_listener frame_listener = {
- frame_callback,
-};
-
static bool wayland_vk_start_frame(struct ra_ctx *ctx)
{
struct vo_wayland_state *wl = ctx->vo->wl;
@@ -189,9 +116,6 @@ static bool wayland_vk_init(struct ra_ctx *ctx)
ra_add_native_resource(ctx->ra, "wl", ctx->vo->wl->display);
- ctx->vo->wl->frame_callback = wl_surface_frame(ctx->vo->wl->surface);
- wl_callback_add_listener(ctx->vo->wl->frame_callback, &frame_listener, ctx->vo->wl);
-
return true;
error:
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index 38b9ca0a8c..7d9dc5f99e 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -141,14 +141,16 @@ static int get_mods(struct vo_wayland_state *wl)
MP_KEY_MODIFIER_META,
};
+ 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))
- return mods[n];
+ modifiers |= mods[n];
}
- return 0;
+ return modifiers;
}
static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
@@ -266,6 +268,7 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
mp_input_put_key(wl->vo->input_ctx, button | state | mpmod);
if (!mp_input_test_dragging(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y) &&
+ (!wl->vo_opts->fullscreen) && (!wl->vo_opts->window_maximized) &&
(button == MP_MBTN_LEFT) && (state == MP_KEY_STATE_DOWN)) {
uint32_t edges;
// Implement an edge resize zone if there are no decorations
@@ -683,7 +686,6 @@ static void data_offer_handle_offer(void *data, struct wl_data_offer *offer,
static void data_offer_source_actions(void *data, struct wl_data_offer *offer, uint32_t source_actions)
{
-
}
static void data_offer_action(void *data, struct wl_data_offer *wl_data_offer, uint32_t dnd_action)
@@ -792,10 +794,62 @@ static const struct wl_data_device_listener data_device_listener = {
data_device_handle_selection,
};
+static void greatest_common_divisor(struct vo_wayland_state *wl, int a, int b) {
+ // euclidean algorithm
+ int larger;
+ int smaller;
+ if (a > b) {
+ larger = a;
+ smaller = b;
+ } else {
+ larger = b;
+ smaller = a;
+ }
+ int remainder = larger - smaller * floor(larger/smaller);
+ if (remainder == 0) {
+ wl->gcd = smaller;
+ } else {
+ greatest_common_divisor(wl, smaller, remainder);
+ }
+}
+
+static void set_geometry(struct vo_wayland_state *wl)
+{
+ struct vo *vo = wl->vo;
+
+ struct vo_win_geometry geo;
+ struct mp_rect screenrc = wl->current_output->geometry;
+ vo_calc_window_geometry(vo, &screenrc, &geo);
+ vo_apply_window_geometry(vo, &geo);
+
+ greatest_common_divisor(wl, vo->dwidth, vo->dheight);
+ wl->reduced_width = vo->dwidth / wl->gcd;
+ wl->reduced_height = vo->dheight / wl->gcd;
+
+ wl->vdparams.x0 = 0;
+ wl->vdparams.y0 = 0;
+ wl->vdparams.x1 = vo->dwidth / wl->scaling;
+ wl->vdparams.y1 = vo->dheight / wl->scaling;
+}
+
+static void rescale_geometry_dimensions(struct vo_wayland_state *wl, double factor)
+{
+ wl->vdparams.x1 *= factor;
+ wl->vdparams.y1 *= factor;
+ wl->window_size.x1 *= factor;
+ wl->window_size.y1 *= factor;
+ if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized) {
+ wl->geometry.x1 *= factor;
+ wl->geometry.y1 *= factor;
+ }
+}
+
static void surface_handle_enter(void *data, struct wl_surface *wl_surface,
struct wl_output *output)
{
struct vo_wayland_state *wl = data;
+ struct mp_rect old_output_geometry = wl->current_output->geometry;
+ struct mp_rect old_geometry = wl->geometry;
wl->current_output = NULL;
struct vo_wayland_output *o;
@@ -807,9 +861,25 @@ static void surface_handle_enter(void *data, struct wl_surface *wl_surface,
}
wl->current_output->has_surface = true;
- if (wl->scaling != wl->current_output->scale)
+ bool force_resize = false;
+
+ if (!mp_rect_equals(&old_output_geometry, &wl->current_output->geometry)) {
+ set_geometry(wl);
+ wl->window_size = wl->vdparams;
+ force_resize = true;
+ }
+
+ if (wl->scaling != wl->current_output->scale && wl->vo_opts->hidpi_window_scale) {
+ double factor = (double)wl->scaling / wl->current_output->scale;
+ wl->scaling = wl->current_output->scale;
+ rescale_geometry_dimensions(wl, factor);
+ }
+
+ if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
+ wl->geometry = wl->window_size;
+
+ if (!mp_rect_equals(&old_geometry, &wl->geometry) || force_resize)
wl->pending_vo_events |= VO_EVENT_RESIZE;
- wl->scaling = wl->current_output->scale;
MP_VERBOSE(wl, "Surface entered output %s %s (0x%x), scale = %i\n", o->make,
o->model, o->id, wl->scaling);
@@ -850,6 +920,79 @@ static const struct wp_presentation_listener pres_listener = {
pres_set_clockid,
};
+static const struct wp_presentation_feedback_listener feedback_listener;
+
+static void feedback_sync_output(void *data, struct wp_presentation_feedback *fback,
+ struct wl_output *output)
+{
+}
+
+static void feedback_presented(void *data, struct wp_presentation_feedback *fback,
+ uint32_t tv_sec_hi, uint32_t tv_sec_lo,
+ uint32_t tv_nsec, uint32_t refresh_nsec,
+ uint32_t seq_hi, uint32_t seq_lo,
+ uint32_t flags)
+{
+ struct vo_wayland_state *wl = data;
+ vo_wayland_sync_shift(wl);
+
+ if (fback)
+ wp_presentation_feedback_destroy(fback);
+
+ // Very similar to oml_sync_control, in this case we assume that every
+ // time the compositor receives feedback, a buffer swap has been already
+ // been performed.
+ //
+ // Notes:
+ // - tv_sec_lo + tv_sec_hi is the equivalent of oml's ust
+ // - seq_lo + seq_hi is the equivalent of oml's msc
+ // - these values are updated everytime the compositor receives feedback.
+
+ int index = last_available_sync(wl);
+ if (index < 0) {
+ queue_new_sync(wl);
+ index = 0;
+ }
+ int64_t sec = (uint64_t) tv_sec_lo + ((uint64_t) tv_sec_hi << 32);
+ wl->sync[index].ust = sec * 1000000LL + (uint64_t) tv_nsec / 1000;
+ wl->sync[index].msc = (uint64_t) seq_lo + ((uint64_t) seq_hi << 32);
+ wl->sync[index].filled = true;
+}
+
+static void feedback_discarded(void *data, struct wp_presentation_feedback *fback)
+{
+}
+
+static const struct wp_presentation_feedback_listener feedback_listener = {
+ feedback_sync_output,
+ feedback_presented,
+ feedback_discarded,
+};
+
+static const struct wl_callback_listener frame_listener;
+
+static void frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct vo_wayland_state *wl = data;
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ wl->frame_callback = wl_surface_frame(wl->surface);
+ wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
+
+ if (wl->presentation) {
+ wl->feedback = wp_presentation_feedback(wl->presentation, wl->surface);
+ wp_presentation_feedback_add_listener(wl->feedback, &feedback_listener, wl);
+ }
+
+ wl->frame_wait = false;
+}
+
+static const struct wl_callback_listener frame_listener = {
+ frame_callback,
+};
+
static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id,
const char *interface, uint32_t ver)
{
@@ -1212,6 +1355,9 @@ int vo_wayland_init(struct vo *vo)
wl->opts = mp_get_config_group(wl, wl->vo->global, &wayland_conf);
wl->display_fd = wl_display_get_fd(wl->display);
+ wl->frame_callback = wl_surface_frame(wl->surface);
+ wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
+
mp_make_wakeup_pipe(wl->wakeup_pipe);
return true;
@@ -1320,27 +1466,32 @@ void vo_wayland_uninit(struct vo *vo)
vo->wl = NULL;
}
-static bool find_output(struct vo_wayland_state *wl, int index)
+static struct vo_wayland_output *find_output(struct vo_wayland_state *wl)
{
- int screen_id = 0;
+ int index = 0;
+ int screen_id = wl->vo_opts->fsscreen_id;
+ char *screen_name = wl->vo_opts->fsscreen_name;
struct vo_wayland_output *output = NULL;
struct vo_wayland_output *fallback_output = NULL;
wl_list_for_each(output, &wl->output_list, link) {
- if (screen_id == 0)
+ if (index == 0)
fallback_output = output;
- if (index == screen_id++)
- wl->current_output = output;
+ if (screen_id == -1 && !screen_name)
+ return output;
+ if (screen_id == -1 && screen_name && !strcmp(screen_name, output->model))
+ return output;
+ if (screen_id == index++)
+ return output;
}
- if (!wl->current_output) {
- if (!fallback_output) {
- MP_ERR(wl, "Screen index %i not found/unavailable!\n", index);
- return 1;
- } else {
- MP_WARN(wl, "Screen index %i not found/unavailable! Falling back to screen 0!\n", index);
- wl->current_output = fallback_output;
- }
+ if (!fallback_output) {
+ MP_ERR(wl, "No screens could be found!\n");
+ return NULL;
+ } else if (wl->vo_opts->fsscreen_id >= 0) {
+ MP_WARN(wl, "Screen index %i not found/unavailable! Falling back to screen 0!\n", screen_id);
+ } else if (wl->vo_opts->fsscreen_name) {
+ MP_WARN(wl, "Screen name %s not found/unavailable! Falling back to screen 0!\n", screen_name);
}
- return 0;
+ return fallback_output;
}
static void toggle_fullscreen(struct vo_wayland_state *wl)
@@ -1348,11 +1499,12 @@ static void toggle_fullscreen(struct vo_wayland_state *wl)
if (!wl->xdg_toplevel)
return;
wl->state_change = true;
- if (wl->vo_opts->fullscreen && wl->vo_opts->fsscreen_id < 0) {
+ 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);
- } else if (wl->vo_opts->fullscreen && wl->vo_opts->fsscreen_id >= 0) {
- find_output(wl, wl->vo_opts->fsscreen_id);
- xdg_toplevel_set_fullscreen(wl->xdg_toplevel, wl->current_output->output);
+ } else if (wl->vo_opts->fullscreen && specific_screen) {
+ struct vo_wayland_output *output = find_output(wl);
+ xdg_toplevel_set_fullscreen(wl->xdg_toplevel, output->output);
} else {
xdg_toplevel_unset_fullscreen(wl->xdg_toplevel);
}
@@ -1378,25 +1530,6 @@ static void do_minimize(struct vo_wayland_state *wl)
xdg_toplevel_set_minimized(wl->xdg_toplevel);
}
-static void greatest_common_divisor(struct vo_wayland_state *wl, int a, int b) {
- // euclidean algorithm
- int larger;
- int smaller;
- if (a > b) {
- larger = a;
- smaller = b;
- } else {
- larger = b;
- smaller = a;
- }
- int remainder = larger - smaller * floor(larger/smaller);
- if (remainder == 0) {
- wl->gcd = smaller;
- } else {
- greatest_common_divisor(wl, smaller, remainder);
- }
-}
-
int vo_wayland_reconfig(struct vo *vo)
{
struct vo_wayland_state *wl = vo->wl;
@@ -1405,33 +1538,24 @@ int vo_wayland_reconfig(struct vo *vo)
MP_VERBOSE(wl, "Reconfiguring!\n");
if (!wl->current_output) {
- int idx = 0;
- if (wl->vo_opts->fullscreen && (wl->vo_opts->fsscreen_id >= 0))
- idx = wl->vo_opts->fsscreen_id;
- if (find_output(wl, idx))
+ wl->current_output = find_output(wl);
+ if (!wl->current_output)
return false;
if (!wl->vo_opts->hidpi_window_scale)
wl->current_output->scale = 1;
wl->scaling = wl->current_output->scale;
+ wl_surface_set_buffer_scale(wl->surface, wl->scaling);
+ wl_surface_commit(wl->surface);
configure = true;
}
- struct vo_win_geometry geo;
- struct mp_rect screenrc = wl->current_output->geometry;
- vo_calc_window_geometry(vo, &screenrc, &geo);
- vo_apply_window_geometry(vo, &geo);
+ set_geometry(wl);
- greatest_common_divisor(wl, vo->dwidth, vo->dheight);
- wl->reduced_width = vo->dwidth / wl->gcd;
- wl->reduced_height = vo->dheight / wl->gcd;
-
- wl->vdparams.x0 = 0;
- wl->vdparams.y0 = 0;
- wl->vdparams.x1 = vo->dwidth / wl->scaling;
- wl->vdparams.y1 = vo->dheight / wl->scaling;
- if (wl->vo_opts->keepaspect && wl->vo_opts->keepaspect_window) {
+ if (wl->vo_opts->keepaspect && wl->vo_opts->keepaspect_window)
wl->window_size = wl->vdparams;
- }
+
+ if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
+ wl->geometry = wl->window_size;
if (wl->vo_opts->fullscreen)
toggle_fullscreen(wl);
@@ -1442,18 +1566,12 @@ int vo_wayland_reconfig(struct vo *vo)
if (wl->vo_opts->window_minimized)
do_minimize(wl);
- wl_surface_set_buffer_scale(wl->surface, wl->scaling);
- wl_surface_commit(wl->surface);
-
if (configure) {
wl->window_size = wl->vdparams;
- wl->geometry = wl->vdparams;
+ wl->geometry = wl->window_size;
wl_display_roundtrip(wl->display);
}
- if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
- wl->geometry = wl->window_size;
-
wl->pending_vo_events |= VO_EVENT_RESIZE;
return true;
@@ -1568,6 +1686,15 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
set_border_decorations(wl, opts->border);
if (opt == &opts->appid)
update_app_id(wl);
+ if (opt == &opts->geometry || opt == &opts->autofit ||
+ opt == &opts->autofit_smaller || opt == &opts->autofit_larger)
+ {
+ set_geometry(wl);
+ wl->window_size = wl->vdparams;
+ if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
+ wl->geometry = wl->window_size;
+ wl->pending_vo_events |= VO_EVENT_RESIZE;
+ }
}
return VO_TRUE;
}
diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h
index 41edcf5d2f..70e2fcbe68 100644
--- a/video/out/wayland_common.h
+++ b/video/out/wayland_common.h
@@ -149,7 +149,6 @@ int vo_wayland_init(struct vo *vo);
int vo_wayland_reconfig(struct vo *vo);
int vo_wayland_control(struct vo *vo, int *events, int request, void *arg);
int last_available_sync(struct vo_wayland_state *wl);
-void vo_wayland_check_events(struct vo *vo);
void vo_wayland_uninit(struct vo *vo);
void vo_wayland_wakeup(struct vo *vo);
void vo_wayland_wait_events(struct vo *vo, int64_t until_time_us);
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
index 92b3539aca..ac551fae8e 100644
--- a/video/out/x11_common.c
+++ b/video/out/x11_common.c
@@ -467,7 +467,8 @@ static void vo_x11_update_screeninfo(struct vo *vo)
struct mp_vo_opts *opts = x11->opts;
bool all_screens = opts->fullscreen && opts->fsscreen_id == -2;
x11->screenrc = (struct mp_rect){.x1 = x11->ws_width, .y1 = x11->ws_height};
- if (opts->screen_id >= -1 && XineramaIsActive(x11->display) && !all_screens)
+ if ((opts->screen_id >= -1 || opts->screen_name) && XineramaIsActive(x11->display) &&
+ !all_screens)
{
int screen = opts->fullscreen ? opts->fsscreen_id : opts->screen_id;
XineramaScreenInfo *screens;
@@ -476,6 +477,23 @@ static void vo_x11_update_screeninfo(struct vo *vo)
if (opts->fullscreen && opts->fsscreen_id == -1)
screen = opts->screen_id;
+ if (screen == -1 && (opts->fsscreen_name || opts->screen_name)) {
+ char *screen_name = opts->fullscreen ? opts->fsscreen_name : opts->screen_name;
+ if (screen_name) {
+ bool screen_found = false;
+ for (int n = 0; n < x11->num_displays; n++) {
+ char *display_name = x11->displays[n].name;
+ if (!strcmp(display_name, screen_name)) {
+ screen = n;
+ screen_found = true;
+ break;
+ }
+ }
+ if (!screen_found)
+ MP_WARN(x11, "Screen name %s not found!\n", screen_name);
+ }
+ }
+
screens = XineramaQueryScreens(x11->display, &num_screens);
if (screen >= num_screens)
screen = num_screens - 1;
@@ -1019,6 +1037,11 @@ static void vo_x11_check_net_wm_state_change(struct vo *vo)
XFree(elems);
}
+ if (opts->window_maximized && !is_maximized && x11->pending_geometry_change) {
+ vo_x11_config_vo_window(vo);
+ x11->pending_geometry_change = false;
+ }
+
opts->window_minimized = is_minimized;
m_config_cache_write_opt(x11->opts_cache, &opts->window_minimized);
opts->window_maximized = is_maximized;
@@ -1820,6 +1843,17 @@ static void vo_x11_minimize(struct vo *vo)
}
}
+static void vo_x11_set_geometry(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+
+ if (x11->opts->window_maximized) {
+ x11->pending_geometry_change = true;
+ } else {
+ vo_x11_config_vo_window(vo);
+ }
+}
+
int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
{
struct vo_x11_state *x11 = vo->x11;
@@ -1853,6 +1887,11 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
vo_x11_minimize(vo);
if (opt == &opts->window_maximized)
vo_x11_maximize(vo);
+ if (opt == &opts->geometry || opt == &opts->autofit ||
+ opt == &opts->autofit_smaller || opt == &opts->autofit_larger)
+ {
+ vo_x11_set_geometry(vo);
+ }
}
return VO_TRUE;
}
diff --git a/video/out/x11_common.h b/video/out/x11_common.h
index 774a76afbe..45c8d04d21 100644
--- a/video/out/x11_common.h
+++ b/video/out/x11_common.h
@@ -111,6 +111,10 @@ struct vo_x11_state {
bool size_changed_during_fs;
bool pos_changed_during_fs;
+ /* The geometry/autofit option was changed while the window was maximized.
+ * Wait until the state changes to resize. */
+ bool pending_geometry_change;
+
XComposeStatus compose_status;
/* XShm stuff */
diff --git a/video/zimg.c b/video/zimg.c
index a73293c8c6..e29d7c4ce7 100644
--- a/video/zimg.c
+++ b/video/zimg.c
@@ -123,6 +123,7 @@ static void mp_zimg_update_from_cmdline(struct mp_zimg_context *ctx)
static zimg_chroma_location_e mp_to_z_chroma(enum mp_chroma_location cl)
{
switch (cl) {
+ case MP_CHROMA_TOPLEFT: return ZIMG_CHROMA_TOP_LEFT;
case MP_CHROMA_LEFT: return ZIMG_CHROMA_LEFT;
case MP_CHROMA_CENTER: return ZIMG_CHROMA_CENTER;
default: return ZIMG_CHROMA_LEFT;
diff --git a/waftools/checks/custom.py b/waftools/checks/custom.py
index 36c1d85d06..964546977b 100644
--- a/waftools/checks/custom.py
+++ b/waftools/checks/custom.py
@@ -114,17 +114,19 @@ def check_cocoa(ctx, dependency_identifier):
return res
-def check_swift(ctx, dependency_identifier):
- minVer = StrictVersion("3.0.2")
- if ctx.env.SWIFT_VERSION:
- if StrictVersion(ctx.env.SWIFT_VERSION) >= minVer:
- ctx.add_optional_message(dependency_identifier,
- 'version found: ' + str(ctx.env.SWIFT_VERSION))
- return True
- ctx.add_optional_message(dependency_identifier,
- "'swift >= " + str(minVer) + "' not found, found " +
- str(ctx.env.SWIFT_VERSION or None))
- return False
+def check_swift(version):
+ def fn(ctx, dependency_identifier):
+ minVer = StrictVersion(version)
+ if ctx.env.SWIFT_VERSION:
+ if StrictVersion(ctx.env.SWIFT_VERSION) >= minVer:
+ ctx.add_optional_message(dependency_identifier,
+ 'version found: ' + str(ctx.env.SWIFT_VERSION))
+ return True
+ ctx.add_optional_message(dependency_identifier,
+ "'swift >= " + str(minVer) + "' not found, found " +
+ str(ctx.env.SWIFT_VERSION or None))
+ return False
+ return fn
def check_egl_provider(minVersion=None, name='egl', check=None):
def fn(ctx, dependency_identifier, **kw):
diff --git a/wscript b/wscript
index b81f1202ec..0ced76244a 100644
--- a/wscript
+++ b/wscript
@@ -182,7 +182,7 @@ main_dependencies = [
'name': '--swift',
'desc': 'macOS Swift build tools',
'deps': 'os-darwin',
- 'func': compose_checks(check_swift, check_macos_sdk('10.10')),
+ 'func': compose_checks(check_swift('4.1'), check_macos_sdk('10.10')),
}, {
'name': '--uwp',
'desc': 'Universal Windows Platform',
@@ -323,8 +323,8 @@ iconv support use --disable-iconv.",
}, {
'name': '--zlib',
'desc': 'zlib',
- 'func': check_libs(['z'],
- check_statement('zlib.h', 'inflate(0, Z_NO_FLUSH)')),
+ 'func': any_check(check_pkg_config('zlib'),
+ check_libs(['z'], check_statement('zlib.h', 'inflate(0, Z_NO_FLUSH)'))),
'req': True,
'fmsg': 'Unable to find development files for zlib.'
}, {
@@ -725,7 +725,7 @@ video_output_features = [
}, {
'name': '--libplacebo',
'desc': 'libplacebo support',
- 'func': check_pkg_config('libplacebo >= 1.18.0'),
+ 'func': check_pkg_config('libplacebo >= 2.72.0'),
}, {
'name': '--vulkan',
'desc': 'Vulkan context support',
@@ -958,6 +958,9 @@ def configure(ctx):
while re.match('\$\{([^}]+)\}', ctx.env[varname]):
ctx.env[varname] = Utils.subst_vars(ctx.env[varname], ctx.env)
+ if ctx.options.LUA_VER:
+ ctx.options.enable_lua = True
+
ctx.parse_dependencies(build_options)
ctx.parse_dependencies(main_dependencies)
ctx.parse_dependencies(libav_dependencies)
@@ -965,9 +968,6 @@ def configure(ctx):
ctx.parse_dependencies(video_output_features)
ctx.parse_dependencies(hwaccel_features)
- if ctx.options.LUA_VER:
- ctx.options.enable_lua = True
-
if ctx.options.SWIFT_FLAGS:
ctx.env.SWIFT_FLAGS.extend(split(ctx.options.SWIFT_FLAGS))