summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml25
-rw-r--r--DOCS/interface-changes.rst12
-rw-r--r--DOCS/man/ao.rst8
-rw-r--r--DOCS/man/console.rst4
-rw-r--r--DOCS/man/input.rst378
-rw-r--r--DOCS/man/mpv.rst26
-rw-r--r--DOCS/man/options.rst147
-rw-r--r--DOCS/man/stats.rst4
-rw-r--r--DOCS/man/vo.rst102
-rw-r--r--RELEASE_NOTES135
-rwxr-xr-xTOOLS/appveyor-install.sh12
-rwxr-xr-xTOOLS/file2string.py11
-rwxr-xr-xTOOLS/matroska.py8
-rw-r--r--VERSION2
-rw-r--r--audio/out/ao_alsa.c29
-rwxr-xr-xbootstrap.py17
-rwxr-xr-xci/build-macos.sh3
-rwxr-xr-xci/build-mingw64.sh11
-rw-r--r--demux/demux.c20
-rw-r--r--demux/demux.h2
-rw-r--r--input/cmd.c13
-rw-r--r--input/input.c11
-rw-r--r--input/input.h2
-rw-r--r--options/options.c4
-rw-r--r--options/options.h2
-rw-r--r--options/path.c1
-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/path-win.c2
-rw-r--r--osdep/terminal-dummy.c4
-rw-r--r--osdep/terminal-unix.c23
-rw-r--r--osdep/terminal-win.c4
-rw-r--r--osdep/terminal.h3
-rw-r--r--player/command.c62
-rw-r--r--player/command.h1
-rw-r--r--player/core.h3
-rw-r--r--player/external_files.c12
-rw-r--r--player/javascript.c49
-rw-r--r--player/javascript/defaults.js1
-rw-r--r--player/loadfile.c17
-rw-r--r--player/lua.c11
-rw-r--r--player/lua/auto_profiles.lua16
-rw-r--r--player/lua/console.lua12
-rw-r--r--player/lua/defaults.lua5
-rw-r--r--player/playloop.c4
-rw-r--r--stream/stream.c3
-rw-r--r--video/csputils.c11
-rw-r--r--video/csputils.h1
-rw-r--r--video/decode/vd_lavc.c2
-rw-r--r--video/out/cocoa_cb_common.swift3
-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.c40
-rw-r--r--video/out/gpu/video_shaders.c83
-rw-r--r--video/out/mac/common.swift134
-rw-r--r--video/out/mac/title_bar.swift4
-rw-r--r--video/out/mac/window.swift20
-rw-r--r--video/out/opengl/context_wayland.c78
-rw-r--r--video/out/placebo/ra_pl.c31
-rw-r--r--video/out/vo.c4
-rw-r--r--video/out/vo_sixel.c580
-rw-r--r--video/out/vo_tct.c10
-rw-r--r--video/out/vo_wlshm.c42
-rw-r--r--video/out/vulkan/context_wayland.c81
-rw-r--r--video/out/wayland_common.c328
-rw-r--r--video/out/wayland_common.h2
-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--waftools/detections/compiler_swift.py6
-rw-r--r--waftools/generators/sources.py2
-rw-r--r--wscript16
-rw-r--r--wscript_build.py1
77 files changed, 2083 insertions, 850 deletions
diff --git a/.travis.yml b/.travis.yml
index b2700a9b12..18946c05e1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,11 +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_HOMEBREW_HASH=7242872d7878f1a4c2706e5837faafcf0782b58d
- os: freebsd
compiler: clang
- os: linux
@@ -53,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
@@ -114,23 +118,22 @@ before_install:
fi
- |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
- remove=$(brew list)
+ remove=$(brew list --formula)
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
- fi
+ brew remove --cask $(brew list --cask)
+ 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 2a7120b10c..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.
@@ -79,6 +87,8 @@ Interface changes
- key/value list options do not accept ":" as item separator anymore,
only ",". This means ":" is always considered part of the value.
- remove deprecated --vo-vdpau-deint option
+ - add `delete-watch-later-config` command to complement
+ `write-watch-later-config`
--- mpv 0.32.0 ---
- change behavior when using legacy option syntax with options that start
with two dashes (``--`` instead of a ``-``). Now, using the recommended
@@ -145,8 +155,6 @@ Interface changes
- change the default of --hwdec to "no" on RPI. The default used to be "mmal"
specifically if 'Raspberry Pi support' was enabled at configure time
(equivalent to --enable-rpi). Use --hwdec=mmal to get the old behavior.
- - add `delete-watch-later-config` command to complement
- `write-watch-later-config`
--- mpv 0.30.0 ---
- add `--d3d11-output-format` to enable explicit selection of a D3D11
swap chain format.
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 d548110c93..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:
@@ -160,19 +160,30 @@ Flat command syntax
This is the syntax used in input.conf, and referred to "input.conf syntax" in
a number of other places.
-``<command> ::= [<prefixes>] <command_name> (<argument>)*``
-``<argument> ::= (<string> | " <quoted_string> " )``
+|
+| ``<command> ::= [<prefixes>] <command_name> (<argument>)*``
+| ``<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
----------------------------
@@ -512,7 +523,7 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
``playback_only`` (``MPV_FORMAT_FLAG``)
Boolean indicating whether the process should be killed when playback
- terminates (optional, default: yes). If enabled, stopping playback
+ terminates (optional, default: true). If enabled, stopping playback
will automatically kill the process, and you can't start it outside of
playback.
@@ -532,7 +543,7 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
Whether to run the process in detached mode (optional, default: no). In
this mode, the process is run in a new process session, and the command
does not wait for the process to terminate. If neither
- ``capture_stdout`` nor ``capture_stderr`` have been set to ``yes``,
+ ``capture_stdout`` nor ``capture_stderr`` have been set to true,
the command returns immediately after the new process has been started,
otherwise the command will read as long as the pipes are open.
@@ -555,8 +566,8 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
``passthrough_stdin`` (``MPV_FORMAT_FLAG``)
If enabled, wire the new process' stdin to mpv's stdin (default: no).
- Before mpv 0.33.0, this argument did not exist, but the default was if
- it was set to ``yes``.
+ Before mpv 0.33.0, this argument did not exist, but the behavior was as
+ if this was set to true.
The command returns the following result (as ``MPV_FORMAT_NODE_MAP``):
@@ -582,13 +593,12 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
process could not be started.
On Windows, ``killed`` is only returned when the process has been
- killed by mpv as a result of ``playback_only`` being set to ``yes``.
+ killed by mpv as a result of ``playback_only`` being set to true.
``killed_by_us`` (``MPV_FORMAT_FLAG``)
- Set to ``yes`` if the process has been killed by mpv, for example as a
- result of ``playback_only`` being set to ``yes``, aborting the command
- (e.g. by ``mp.abort_async_command()``), or if the player is about to
- exit.
+ Whether the process has been killed by mpv, for example as a result of
+ ``playback_only`` being set to true, aborting the command (e.g. by
+ ``mp.abort_async_command()``), or if the player is about to exit.
Note that the command itself will always return success as long as the
parameters are correct. Whether the process could be spawned or whether
@@ -838,8 +848,8 @@ Input Commands that are Possibly Subject to Change
Append the new filter chain to the previous one.
<toggle>
- Check if the given filter (with the exact parameters) is already
- in the video chain. If yes, remove the filter. If no, add the filter.
+ Check if the given filter (with the exact parameters) is already in the
+ video chain. If it is, remove the filter. If it isn't, add the filter.
(If several filters are passed to the command, this is done for
each filter.)
@@ -1119,14 +1129,14 @@ Input Commands that are Possibly Subject to Change
between different OSD formats and builtin OSD is handled.
``hidden``
- If set to ``yes``/true, do not display this (default: no).
+ If set to true, do not display this (default: false).
``compute_bounds``
- If set to ``yes``/true, attempt to determine bounds and write them to
- the command's result value as ``x0``, ``x1``, ``y0``, ``y1`` rectangle
- (default: no). If the rectangle is empty, not known, or somehow
- degenerate, it is not set. ``x1``/``y1`` is the coordinate of the bottom
- exclusive corner of the rectangle.
+ If set to true, attempt to determine bounds and write them to the
+ command's result value as ``x0``, ``x1``, ``y0``, ``y1`` rectangle
+ (default: false). If the rectangle is empty, not known, or somehow
+ degenerate, it is not set. ``x1``/``y1`` is the coordinate of the
+ bottom exclusive corner of the rectangle.
The result value may depend on the VO window size, and is based on the
last known window size at the time of the call. This means the results
@@ -1532,6 +1542,17 @@ This list uses the event name field value, and the C API symbol in brackets:
``audio-reconfig`` (``MPV_EVENT_AUDIO_RECONFIG``)
Happens on audio output or filter reconfig.
+``property-change`` (``MPV_EVENT_PROPERTY_CHANGE``)
+ Happens when a property that is being observed changes value.
+
+ The event has the following fields:
+
+ ``name``
+ The name of the property.
+
+ ``data``
+ The new value of the property.
+
The following events also happen, but are deprecated: ``tracks-changed``,
``track-switched``, ``pause``, ``unpause``, ``metadata-update``, ``idle``,
``tick``, ``chapter-change``. Use ``mpv_observe_property()``
@@ -1753,6 +1774,9 @@ Property list
same name, or which have very different behavior from the options are
documented below.
+ Properties marked as (RW) are writeable, while those that aren't are
+ read-only.
+
``audio-speed-correction``, ``video-speed-correction``
Factor multiplied with ``speed`` at which the player attempts to play the
file. Usually it's exactly 1. (Display sync mode will make this useful.)
@@ -1761,7 +1785,7 @@ Property list
being ``(raw - 1) * 100`` for the given raw property value.
``display-sync-active``
- Return whether ``--video-sync=display`` is actually active.
+ Whether ``--video-sync=display`` is actually active.
``filename``
Currently played file, with path stripped. If this is an URL, try to undo
@@ -1901,7 +1925,7 @@ Property list
Remaining length of the file in seconds. Note that the file duration is not
always exactly known, so this is an estimate.
-``audio-pts`` (R)
+``audio-pts``
Current audio playback position in current file in seconds. Unlike time-pos,
this updates more often than once per frame. For audio-only files, it is
mostly equivalent to time-pos, while for video-only files this property is
@@ -1952,12 +1976,12 @@ Property list
Number of editions. If there are no editions, this can be 0 or 1 (1
if there's a useless dummy edition).
- ``edition-list/N/id``
+ ``edition-list/N/id`` (RW)
Edition ID as integer. Use this to set the ``edition`` property.
Currently, this is the same as the edition index.
``edition-list/N/default``
- ``yes`` if this is the default edition, ``no`` otherwise.
+ Whether this is the default edition.
``edition-list/N/title``
Edition title as stored in the file. Not always available.
@@ -2044,21 +2068,21 @@ Property list
Equivalent to ``vf-metadata/<filter-label>``, but for audio filters.
``idle-active``
- Return ``yes`` if no file is loaded, but the player is staying around
+ Returns ``yes``/true if no file is loaded, but the player is staying around
because of the ``--idle`` option.
(Renamed from ``idle``.)
``core-idle``
- Return ``yes`` if the playback core is paused, otherwise ``no``. This can
- be different ``pause`` in special situations, such as when the player
- pauses itself due to low network cache.
+ Whether the playback core is paused. This can differ from ``pause`` in
+ special situations, such as when the player pauses itself due to low
+ network cache.
- This also returns ``yes`` if playback is restarting or if nothing is
- playing at all. In other words, it's only ``no`` if there's actually
+ This also returns ``yes``/true if playback is restarting or if nothing is
+ playing at all. In other words, it's only ``no``/false if there's actually
video playing. (Behavior since mpv 0.7.0.)
-``cache-speed`` (R)
+``cache-speed``
Current I/O read speed between the cache and the lower layer (like network).
This gives the number bytes per seconds over a 1 second window (using
the type ``MPV_FORMAT_INT64`` for the client API).
@@ -2076,14 +2100,13 @@ Property list
data in demuxer.
``demuxer-cache-idle``
- Returns ``yes`` if the demuxer is idle, which means the demuxer cache is
- filled to the requested amount, and is currently not reading more data.
+ Whether the demuxer is idle, which means that the demuxer cache is filled
+ to the requested amount, and is currently not reading more data.
``demuxer-cache-state``
- Various undocumented or half-documented things.
-
Each entry in ``seekable-ranges`` represents a region in the demuxer cache
- that can be seeked to. If there are multiple demuxers active, this only
+ that can be seeked to, with a ``start`` and ``end`` fields containing the
+ respective timestamps. If there are multiple demuxers active, this only
returns information about the "main" demuxer, but might be changed in
future to return unified information about all demuxers. The ranges are in
arbitrary order. Often, ranges will overlap for a bit, before being joined.
@@ -2098,8 +2121,8 @@ Property list
points to the beginning of the stream (BOF). This implies you cannot seek
before this position at all. ``eof-cached`` indicates whether the seek range
with the highest timestamp points to the end of the stream (EOF). If both
- ``bof-cached`` and ``eof-cached`` are set to ``yes``, and there's only 1
- cache range, the entire stream is cached.
+ ``bof-cached`` and ``eof-cached`` are true, and there's only 1 cache range,
+ the entire stream is cached.
``fw-bytes`` is the number of bytes of packets buffered in the range
starting from the current decoding position. This is a rough estimate
@@ -2108,7 +2131,13 @@ Property list
``file-cache-bytes`` is the number of bytes stored in the file cache. This
includes all overhead, and possibly unused data (like pruned data). This
- member is missing if the file cache is not active.
+ member is missing if the file cache wasn't enabled with
+ ``--cache-on-disk=yes``.
+
+ ``cache-end`` is ``demuxer-cache-time``. Missing if unavailable.
+
+ ``reader-pts`` is the approximate timestamp of the start of the buffered
+ range. Missing if unavailable.
``cache-duration`` is ``demuxer-cache-duration``. Missing if unavailable.
@@ -2131,58 +2160,59 @@ Property list
"eof-cached" MPV_FORMAT_FLAG
"fw-bytes" MPV_FORMAT_INT64
"file-cache-bytes" MPV_FORMAT_INT64
+ "cache-end" MPV_FORMAT_DOUBLE
+ "reader-pts" MPV_FORMAT_DOUBLE
"cache-duration" MPV_FORMAT_DOUBLE
"raw-input-rate" MPV_FORMAT_INT64
Other fields (might be changed or removed in the future):
``eof``
- True if the reader thread has hit the end of the file.
+ Whether the reader thread has hit the end of the file.
``underrun``
- True if the reader thread could not satisfy a decoder's request for a
+ Whether the reader thread could not satisfy a decoder's request for a
new packet.
``idle``
- True if the thread is currently not reading.
+ Whether the thread is currently not reading.
``total-bytes``
Sum of packet bytes (plus some overhead estimation) of the entire packet
queue, including cached seekable ranges.
``demuxer-via-network``
- Returns ``yes`` if the stream demuxed via the main demuxer is most likely
- played via network. What constitutes "network" is not always clear, might
- be used for other types of untrusted streams, could be wrong in certain
- cases, and its definition might be changing. Also, external files (like
- separate audio files or streams) do not influence the value of this
- property (currently).
+ Whether the stream demuxed via the main demuxer is most likely played via
+ network. What constitutes "network" is not always clear, might be used for
+ other types of untrusted streams, could be wrong in certain cases, and its
+ definition might be changing. Also, external files (like separate audio
+ files or streams) do not influence the value of this property (currently).
-``demuxer-start-time`` (R)
- Returns the start time reported by the demuxer in fractional seconds.
+``demuxer-start-time``
+ The start time reported by the demuxer in fractional seconds.
``paused-for-cache``
- Returns ``yes`` when playback is paused because of waiting for the cache.
+ Whether playback is paused because of waiting for the cache.
``cache-buffering-state``
- Return the percentage (0-100) of the cache fill status until the player
- will unpause (related to ``paused-for-cache``).
+ The percentage (0-100) of the cache fill status until the player will
+ unpause (related to ``paused-for-cache``).
``eof-reached``
- Returns ``yes`` if end of playback was reached, ``no`` otherwise. Note
- that this is usually interesting only if ``--keep-open`` is enabled,
- since otherwise the player will immediately play the next file (or exit
- or enter idle mode), and in these cases the ``eof-reached`` property will
- logically be cleared immediately after it's set.
+ Whether the end of playback was reached. Note that this is usually
+ interesting only if ``--keep-open`` is enabled, since otherwise the player
+ will immediately play the next file (or exit or enter idle mode), and in
+ these cases the ``eof-reached`` property will logically be cleared
+ immediately after it's set.
``seeking``
- Returns ``yes`` if the player is currently seeking, or otherwise trying
- to restart playback. (It's possible that it returns ``yes`` while a file
- is loaded. This is because the same underlying code is used for seeking and
+ Whether the player is currently seeking, or otherwise trying to restart
+ playback. (It's possible that it returns ``yes``/true while a file is
+ loaded. This is because the same underlying code is used for seeking and
resyncing.)
``mixer-active``
- Return ``yes`` if the audio mixer is active, ``no`` otherwise.
+ Whether the audio mixer is active.
This option is relatively useless. Before mpv 0.18.1, it could be used to
infer behavior of the ``volume`` property.
@@ -2245,14 +2275,14 @@ Property list
Same as ``audio-params``, but the format of the data written to the audio
API.
-``colormatrix`` (R)
+``colormatrix``
Redirects to ``video-params/colormatrix``. This parameter (as well as
similar ones) can be overridden with the ``format`` video filter.
-``colormatrix-input-range`` (R)
+``colormatrix-input-range``
See ``colormatrix``.
-``colormatrix-primaries`` (R)
+``colormatrix-primaries``
See ``colormatrix``.
``hwdec`` (RW)
@@ -2268,8 +2298,8 @@ Property list
this purpose.
``hwdec-current``
- Return the current hardware decoding in use. If decoding is active, return
- one of the values used by the ``hwdec`` option/property. ``no`` indicates
+ The current hardware decoding in use. If decoding is active, return one of
+ the values used by the ``hwdec`` option/property. ``no``/false indicates
software decoding. If no decoder is loaded, the property is unavailable.
``hwdec-interop``
@@ -2413,12 +2443,20 @@ Property list
redrawing and frame display being somewhat disconnected, and you might
have to pause and force a redraw.
- Sub-properties::
+ This has a number of sub-properties:
+
+ ``video-frame-info/picture-type``
+ The type of the picture. It can be "I" (intra), "P" (predicted), "B"
+ (bi-dir predicted) or unavailable.
+
+ ``video-frame-info/interlaced``
+ Whether the content of the frame is interlaced.
- video-frame-info/picture-type
- video-frame-info/interlaced
- video-frame-info/tff
- video-frame-info/repeat
+ ``video-frame-info/tff``
+ If the content is interlaced, whether the top field is displayed first.
+
+ ``video-frame-info/repeat``
+ Whether the frame must be delayed when decoding.
``container-fps``
Container FPS. This can easily contain bogus values. For videos that use
@@ -2456,7 +2494,8 @@ Property list
ways. The property is unavailable if no video is active.
``focused``
- Whether the window has focus. Currently works only on X11 and Wayland.
+ Whether the window has focus. Currently works only on X11, Wayland and
+ macOS.
``display-names``
Names of the displays that the mpv window covers. On X11, these
@@ -2480,9 +2519,9 @@ Property list
versions, it returned the ``--display-fps`` option value.
``estimated-display-fps``
- Only available if display-sync mode (as selected by ``--video-sync``) is
- active. Returns the actual rate at which display refreshes seem to occur,
- measured by system time.
+ The actual rate at which display refreshes seem to occur, measured by
+ system time. Only available if display-sync mode (as selected by
+ ``--video-sync``) is active.
``vsync-jitter``
Estimated deviation factor of the vsync duration.
@@ -2518,31 +2557,45 @@ Property list
Has the following sub-properties (which can be read as ``MPV_FORMAT_NODE``
or Lua table with ``mp.get_property_native``):
- ``w``
+ ``osd-dimensions/w``
Size of the VO window in OSD render units (usually pixels, but may be
scaled pixels with VOs like ``xv``).
- ``h``
+ ``osd-dimensions/h``
Size of the VO window in OSD render units,
- ``par``
+ ``osd-dimensions/par``
Pixel aspect ratio of the OSD (usually 1).
- ``aspect``
+ ``osd-dimensions/aspect``
Display aspect ratio of the VO window. (Computing from the properties
above.)
- ``mt``, ``mb``, ``ml``, ``mr``
+ ``osd-dimensions/mt``, ``osd-dimensions/mb``, ``osd-dimensions/ml``, ``osd-dimensions/mr``
OSD to video margins (top, bottom, left, right). This describes the
area into which the video is rendered.
Any of these properties may be unavailable or set to dummy values if the
VO window is not created or visible.
+``mouse-pos``
+ Read-only - last known mouse position, normalizd to OSD dimensions.
+
+ Has the following sub-properties (which can be read as ``MPV_FORMAT_NODE``
+ or Lua table with ``mp.get_property_native``):
+
+ ``mouse-pos/x``, ``mouse-pos/y``
+ Last known coordinates of the mouse pointer.
+
+ ``mouse-pos/hover``
+ Boolean - whether the mouse pointer hovers the video window. The
+ coordinates should be ignored when this value is false, because the
+ video backends update them only when the pointer hovers the window.
+
``sub-text``
- Return the current subtitle text regardless of sub visibility.
- Formatting is stripped. If the subtitle is not text-based
- (i.e. DVD/BD subtitles), an empty string is returned.
+ The current subtitle text regardless of sub visibility. Formatting is
+ stripped. If the subtitle is not text-based (i.e. DVD/BD subtitles), an
+ empty string is returned.
This property is experimental and might be removed in the future.
@@ -2560,15 +2613,15 @@ Property list
This property is experimental and might be removed in the future.
``sub-start``
- Return the current subtitle start time (in seconds). If there's multiple
- current subtitles, returns the first start time. If no current subtitle is
- present null is returned instead.
+ The current subtitle start time (in seconds). If there's multiple current
+ subtitles, returns the first start time. If no current subtitle is present
+ null is returned instead.
``sub-end``
- Return the current subtitle end time (in seconds). If there's multiple
- current subtitles, return the last end time. If no current subtitle is
- present, or if it's present but has unknown or incorrect duration, null
- is returned instead.
+ The current subtitle end time (in seconds). If there's multiple current
+ subtitles, return the last end time. If no current subtitle is present, or
+ if it's present but has unknown or incorrect duration, null is returned
+ instead.
``playlist-pos`` (RW)
Current position on playlist. The first entry is on position 0. Writing to
@@ -2642,12 +2695,12 @@ Property list
Filename of the Nth entry.
``playlist/N/playing``
- ``yes`` if the ``playlist-playing-pos`` property points to this entry,
- unavailable or ``no`` otherwise.
+ ``yes``/true if the ``playlist-playing-pos`` property points to this
+ entry, ``no``/false or unavailable otherwise.
``playlist/N/current``
- ``yes`` if the ``playlist-current-pos`` property points to this entry,
- unavailable or ``no`` otherwise.
+ ``yes``/true if the ``playlist-current-pos`` property points to this
+ entry, ``no``/false or unavailable otherwise.
``playlist/N/title``
Name of the Nth entry. Only available if the playlist file contains
@@ -2705,32 +2758,33 @@ Property list
Track language as identified by the file. Not always available.
``track-list/N/albumart``
- ``yes`` if this is a video track that consists of a single picture,
- ``no`` or unavailable otherwise. This is used for video tracks that are
- really attached pictures in audio files.
+ ``yes``/true if this is a video track that consists of a single
+ picture, ``no``/false or unavailable otherwise. This is used for video
+ tracks that are really attached pictures in audio files.
``track-list/N/default``
- ``yes`` if the track has the default flag set in the file, ``no``
- otherwise.
+ ``yes``/true if the track has the default flag set in the file,
+ ``no``/false or unavailable otherwise.
``track-list/N/forced``
- ``yes`` if the track has the forced flag set in the file, ``no``
- otherwise.
+ ``yes``/true if the track has the forced flag set in the file,
+ ``no``/false or unavailable otherwise.
``track-list/N/codec``
The codec name used by this track, for example ``h264``. Unavailable
in some rare cases.
``track-list/N/external``
- ``yes`` if the track is an external file, ``no`` otherwise. This is
- set for separate subtitle files.
+ ``yes``/true if the track is an external file, ``no``/false or
+ unavailable otherwise. This is set for separate subtitle files.
``track-list/N/external-filename``
The filename if the track is from an external file, unavailable
otherwise.
``track-list/N/selected``
- ``yes`` if the track is currently decoded, ``no`` otherwise.
+ ``yes``/true if the track is currently decoded, ``no``/false or
+ unavailable otherwise.
``track-list/N/main-selection``
It indicates the selection order of tracks for the same type.
@@ -2893,20 +2947,20 @@ Property list
It's also possible to write the property using this format.
``seekable``
- Return whether it's generally possible to seek in the current file.
+ Whether it's generally possible to seek in the current file.
``partially-seekable``
- Return ``yes`` if the current file is considered seekable, but only because
- the cache is active. This means small relative seeks may be fine, but larger
- seeks may fail anyway. Whether a seek will succeed or not is generally not
- known in advance.
+ Whether the current file is considered seekable, but only because the cache
+ is active. This means small relative seeks may be fine, but larger seeks
+ may fail anyway. Whether a seek will succeed or not is generally not known
+ in advance.
- If this property returns true, ``seekable`` will also return true.
+ If this property returns ``yes``/true, so will ``seekable``.
``playback-abort``
- Return whether playback is stopped or is to be stopped. (Useful in obscure
- situations like during ``on_load`` hook processing, when the user can
- stop playback, but the script has to explicitly end processing.)
+ Whether playback is stopped or is to be stopped. (Useful in obscure
+ situations like during ``on_load`` hook processing, when the user can stop
+ playback, but the script has to explicitly end processing.)
``cursor-autohide`` (RW)
See ``--cursor-autohide``. Setting this to a new value will always update
@@ -2938,9 +2992,9 @@ Property list
A list of tags can be found here: http://docs.aegisub.org/latest/ASS_Tags/
``vo-configured``
- Return whether the VO is configured right now. Usually this corresponds to
- whether the video window is visible. If the ``--force-window`` option is
- used, this is usually always returns ``yes``.
+ Whether the VO is configured right now. Usually this corresponds to whether
+ the video window is visible. If the ``--force-window`` option is used, this
+ usually always returns ``yes``/true.
``vo-passes``
Contains introspection about the VO's active render passes and their
@@ -3042,9 +3096,9 @@ Property list
These properties shouldn't be used anymore.
``audio-device-list``
- Return the list of discovered audio devices. This is mostly for use with
- the client API, and reflects what ``--audio-device=help`` with the command
- line player returns.
+ The list of discovered audio devices. This is mostly for use with the
+ client API, and reflects what ``--audio-device=help`` with the command line
+ player returns.
When querying the property with the client API using ``MPV_FORMAT_NODE``,
or with Lua ``mp.get_property_native``, this will return a mpv_node with
@@ -3112,8 +3166,8 @@ Property list
same key at the same time.)
``working-directory``
- Return the working directory of the mpv process. Can be useful for JSON IPC
- users, because the command line player usually works with relative paths.
+ The working directory of the mpv process. Can be useful for JSON IPC users,
+ because the command line player usually works with relative paths.
``protocol-list``
List of protocol prefixes potentially recognized by the player. They are
@@ -3162,30 +3216,30 @@ Property list
List of `Key names`_, same as output by ``--input-keylist``.
``mpv-version``
- Return the mpv version/copyright string. Depending on how the binary was
- built, it might contain either a release version, or just a git hash.
+ The mpv version/copyright string. Depending on how the binary was built, it
+ might contain either a release version, or just a git hash.
``mpv-configuration``
- Return the configuration arguments which were passed to the build system
+ The configuration arguments which were passed to the build system
(typically the way ``./waf configure ...`` was invoked).
``ffmpeg-version``
- Return the contents of the ``av_version_info()`` API call. This is a string
- which identifies the build in some way, either through a release version
- number, or a git hash. This applies to Libav as well (the property is
- still named the same.) This property is unavailable if mpv is linked against
- older FFmpeg and Libav versions.
+ The contents of the ``av_version_info()`` API call. This is a string which
+ identifies the build in some way, either through a release version number,
+ or a git hash. This applies to Libav as well (the property is still named
+ the same.) This property is unavailable if mpv is linked against older
+ FFmpeg and Libav versions.
``libass-version``
- Return the value of ``ass_library_version()``. This is an integer, encoded
- in a somewhat weird form (apparently "hex BCD"), indicating the release
- version of the libass library linked to mpv.
+ The value of ``ass_library_version()``. This is an integer, encoded in a
+ somewhat weird form (apparently "hex BCD"), indicating the release version
+ of the libass library linked to mpv.
``options/<name>`` (RW)
- Read-only access to value of option ``--<name>``. Most options can be
- changed at runtime by writing to this property. Note that many options
- require reloading the file for changes to take effect. If there is an
- equivalent property, prefer setting the property instead.
+ The value of option ``--<name>``. Most options can be changed at runtime by
+ writing to this property. Note that many options require reloading the file
+ for changes to take effect. If there is an equivalent property, prefer
+ setting the property instead.
There shouldn't be any reason to access ``options/<name>`` instead of
``<name>``, except in situations in which the properties have different
@@ -3209,19 +3263,19 @@ Property list
sub-properties - they may change radically in the feature.
``option-info/<name>/name``
- Returns the name of the option.
+ The name of the option.
``option-info/<name>/type``
- Return the name of the option type, like ``String`` or ``Integer``.
- For many complex types, this isn't very accurate.
+ The name of the option type, like ``String`` or ``Integer``. For many
+ complex types, this isn't very accurate.
``option-info/<name>/set-from-commandline``
- Return ``yes`` if the option was set from the mpv command line,
- ``no`` otherwise. What this is set to if the option is e.g. changed
- at runtime is left undefined (meaning it could change in the future).
+ Whether the option was set from the mpv command line. What this is set
+ to if the option is e.g. changed at runtime is left undefined (meaning
+ it could change in the future).
``option-info/<name>/set-locally``
- Return ``yes`` if the option was set per-file. This is the case with
+ Whether the option was set per-file. This is the case with
automatically loaded profiles, file-dir configs, and other cases. It
means the option value will be restored to the value before playback
start when playback ends.
@@ -3242,26 +3296,26 @@ Property list
available.
``property-list``
- Return the list of top-level properties.
+ The list of top-level properties.
``profile-list``
- Return the list of profiles and their contents. This is highly
- implementation-specific, and may change any time. Currently, it returns
- an array of options for each profile. Each option has a name and a value,
- with the value currently always being a string. Note that the options array
- is not a map, as order matters and duplicate entries are possible. Recursive
+ The list of profiles and their contents. This is highly
+ implementation-specific, and may change any time. Currently, it returns an
+ array of options for each profile. Each option has a name and a value, with
+ the value currently always being a string. Note that the options array is
+ not a map, as order matters and duplicate entries are possible. Recursive
profiles are not expanded, and show up as special ``profile`` options.
``command-list``
- Return the list of input commands. This returns an array of maps, where
- each map node represents a command. This map currently only has a single
- entry: ``name`` for the name of the command. (This property is supposed to
- be a replacement for ``--input-cmdlist``. The option dumps some more
+ The list of input commands. This returns an array of maps, where each map
+ node represents a command. This map currently only has a single entry:
+ ``name`` for the name of the command. (This property is supposed to be a
+ replacement for ``--input-cmdlist``. The option dumps some more
information, but it's a valid feature request to extend this property if
needed.)
``input-bindings``
- Return list of current input key bindings. This returns an array of maps,
+ The list of current input key bindings. This returns an array of maps,
where each map node represents a binding for a single key/command. This map
has the following entries:
diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst
index 46c0762fad..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,10 @@ 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
================ ===============================================================
@@ -779,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
@@ -1258,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
@@ -1296,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 b25c1f8481..16625fe0c4 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -236,27 +236,40 @@ Playback Control
The value ``no`` is a deprecated alias for ``auto``.
``--playlist=<filename>``
- Play files according to a playlist file (Supports some common formats. If
+ Play files according to a playlist file. Supports some common formats. If
no format is detected, it will be treated as list of files, separated by
- newline characters. Note that XML playlist formats are not supported.)
+ newline characters. You may need this option to load plaintext files as
+ a playlist. Note that XML playlist formats are not supported.
- You can play playlists directly and without this option, however, this
- option disables any security mechanisms that might be in place. You may
- also need this option to load plaintext files as playlist.
+ This option forces ``--demuxer=playlist`` to interpret the playlist file.
+ Some playlist formats, notably CUE and optical disc formats, need to use
+ different demuxers and will not work with this option. They still can be
+ played directly, without using this option.
+
+ You can play playlists directly, without this option. Before mpv version
+ 0.31.0, this option disabled any security mechanisms that might be in
+ place, but since 0.31.0 it uses the same security mechanisms as playing a
+ playlist file directly. If you trust the playlist file, you can disable
+ any security checks with ``--load-unsafe-playlists``. Because playlists
+ can load other playlist entries, consider applying this option only to the
+ playlist itself and not its entries, using something along these lines:
+
+ ``mpv --{ --playlist=filename --load-unsafe-playlists --}``
.. warning::
- The way mpv uses playlist files via ``--playlist`` is not safe against
- maliciously constructed files. Such files may trigger harmful actions.
- This has been the case for all mpv and MPlayer versions, but
- unfortunately this fact was not well documented earlier, and some people
- have even misguidedly recommended use of ``--playlist`` with untrusted
- sources. Do NOT use ``--playlist`` with random internet sources or files
- you do not trust!
+ The way older versions of mpv played playlist files via ``--playlist``
+ was not safe against maliciously constructed files. Such files may
+ trigger harmful actions. This has been the case for all verions of
+ mpv prior to 0.31.0, and all MPlayer versions, but unfortunately this
+ fact was not well documented earlier, and some people have even
+ misguidedly recommended the use of ``--playlist`` with untrusted
+ sources. Do NOT use ``--playlist`` with random internet sources or
+ files you do not trust if you are not sure your mpv is at least 0.31.0.
- Playlist can contain entries using other protocols, such as local files,
- or (most severely), special protocols like ``avdevice://``, which are
- inherently unsafe.
+ In particular, playlists can contain entries using protocols other than
+ local files, such as special protocols like ``avdevice://`` (which are
+ inherently unsafe).
``--chapter-merge-threshold=<number>``
Threshold for merging almost consecutive ordered chapter parts in
@@ -337,9 +350,6 @@ Playback Control
location. (Instead, the behavior is as if the playlist entries were provided
directly to mpv command line or ``loadfile`` command.)
- Note that ``--playlist`` always loads all entries, so you use that instead
- if you really have the need for this functionality.
-
``--access-references=<yes|no>``
Follow any references in the file being opened (default: yes). Disabling
this is helpful if the file is automatically scanned (e.g. thumbnail
@@ -382,6 +392,11 @@ Playback Control
difference between the two option is that this option performs a seek on
loop, instead of reloading the file.
+ Note that ``--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>``
@@ -1186,9 +1201,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)
@@ -1596,7 +1611,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``. 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.
@@ -2369,7 +2384,7 @@ Subtitles
``--sub-ass-vsfilter-blur-compat=<yes|no>``
Scale ``\blur`` tags by video resolution instead of script resolution
- (disabled by default). This is bug in VSFilter, which according to some,
+ (enabled by default). This is bug in VSFilter, which according to some,
can't be fixed anymore in the name of compatibility.
Note that this uses the actual video resolution for calculating the
@@ -2789,7 +2804,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
@@ -2829,6 +2844,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.
@@ -2843,12 +2864,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).
@@ -2946,7 +2973,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.
@@ -2996,9 +3023,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.
@@ -3166,14 +3193,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>``
@@ -3206,8 +3233,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.
@@ -3689,15 +3716,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.
@@ -3842,7 +3871,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``
@@ -5784,7 +5813,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
@@ -5793,14 +5822,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
@@ -5881,7 +5910,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>``
@@ -5890,6 +5919,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
@@ -5908,7 +5947,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
@@ -6003,7 +6042,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
@@ -6352,13 +6391,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.
@@ -6385,7 +6424,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/stats.rst b/DOCS/man/stats.rst
index 4f9496f4de..2cf730f673 100644
--- a/DOCS/man/stats.rst
+++ b/DOCS/man/stats.rst
@@ -212,6 +212,10 @@ are missing.
Memory usage is approximate and does not reflect internal fragmentation.
+JS scripts memory reporting is disabled by default because collecting the data
+at the JS side has an overhead. It can be enabled by exporting the env var
+``MPV_LEAK_REPORT=1`` before starting mpv, and will increase JS memory usage.
+
If entries have ``/time`` and ``/cpu`` variants, the former gives the real time
(monotonic clock), while the latter the thread CPU time (only if the
corresponding pthread API works and is supported).
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index 570e244e5c..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.
@@ -363,6 +363,104 @@ Available video output drivers are:
``--vo-tct-256=<yes|no>`` (default: no)
Use 256 colors - for terminals which don't support true color.
+``sixel``
+ Graphical output for the terminal, using sixels. Tested with ``mlterm`` and
+ ``xterm``.
+
+ Note: the Sixel image output is not synchronized with other terminal output
+ from mpv, which can lead to broken images. The option ``--really-quiet``
+ can help with that, and is recommended.
+
+ You may need to use ``--profile=sw-fast`` to get decent performance.
+
+ Note: at the time of writing, ``xterm`` does not enable sixel by default -
+ launching it as ``xterm -ti 340`` is one way to enable it. Also, ``xterm``
+ does not display images bigger than 1000x1000 pixels by default.
+
+ To render and align sixel images correctly, mpv needs to know the terminal
+ size both in cells and in pixels. By default it tries to use values which
+ the terminal reports, however, due to differences between terminals this is
+ an error-prone process which cannot be automated with certainty - some
+ terminals report the size in pixels including the padding - e.g. ``xterm``,
+ while others report the actual usable number of pixels - like ``mlterm``.
+ Additionally, they may behave differently when maximized or in fullscreen,
+ and mpv cannot detect this state using standard methods.
+
+ Sixel size and alignment options:
+
+ ``--vo-sixel-cols=<columns>``, ``--vo-sixel-rows=<rows>`` (default: 0)
+ Specify the terminal size in character cells, otherwise (0) read it
+ from the terminal, or fall back to 80x25. Note that mpv doesn't use the
+ the last row with sixel because this seems to result in scrolling.
+
+ ``--vo-sixel-width=<width>``, ``--vo-sixel-height=<height>`` (default: 0)
+ Specify the available size in pixels, otherwise (0) read it from the
+ terminal, or fall back to 320x240. Other than excluding the last line,
+ the height is also further rounded down to a multiple of 6 (sixel unit
+ height) to avoid overflowing below the designated size.
+
+ ``--vo-sixel-left=<col>``, ``--vo-sixel-top=<row>`` (default: 0)
+ Specify the position in character cells where the image starts (1 is
+ the first column or row). If 0 (default) then try to automatically
+ determine it according to the other values and the image aspect ratio
+ and zoom.
+
+ ``--vo-sixel-pad-x=<pad_x>``, ``--vo-sixel-pad-y=<pad_y>`` (default: -1)
+ Used only when mpv reads the size in pixels from the terminal.
+ Specify the number of padding pixels (on one side) which are included
+ at the size which the terminal reports. If -1 (default) then the number
+ of pixels is rounded down to a multiple of number of cells (per axis),
+ 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 (Default)
+ Let libsixel choose the dithering method.
+ none
+ Don't diffuse
+ atkinson
+ Diffuse with Bill Atkinson's method.
+ fs
+ Diffuse with Floyd-Steinberg method
+ jajuni
+ Diffuse with Jarvis, Judice & Ninke method
+ stucki
+ Diffuse with Stucki's method
+ burkes
+ Diffuse with Burkes' method
+ arithmetic
+ Positionally stable arithmetic dither
+ xor
+ Positionally stable arithmetic xor based dither
+
+ ``--vo-sixel-fixedpalette=<yes|no>`` (default: yes)
+ Use libsixel's built-in static palette using the XTERM256 profile
+ for dither. Fixed palette uses 256 colors for dithering. Note that
+ using ``no`` (at the time of writing) will slow down ``xterm``.
+
+ ``--vo-sixel-reqcolors=<colors>`` (default: 256)
+ 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)
+ 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``). 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
takes the frame number padded with leading zeros as name.
@@ -400,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/RELEASE_NOTES b/RELEASE_NOTES
index 2c336364d4..b006055707 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -1,8 +1,12 @@
-Release 0.32.0
+Release 0.33.0
==============
This release requires FFmpeg 4.0 or newer.
+For packagers: Note that mpv's build system is only supported on Python 3.
+If you use the `bootstrap.py` script it will take care of this, otherwise you
+need to explicitly invoke the build system using `python3 waf`.
+
Features
--------
@@ -10,25 +14,53 @@ Features
Added
~~~~~
-- stream_libarchive: enable RAR5 support
-- bash completion: add initial implementation
-- cocoa-cb: add support for forcing the dedicated GPU for rendering
-- cocoa-cb: add pinch to resize window gesture
-- w32_common: support minimizing/maximizing using osc window controls
+- scripting: load scripts from directories
+- mac: activate logging when started from the bundle
+- ytdl_hook.lua: delay load subtitles
+- sub: add an option to filter subtitles by regex
+- scripting: add a way to run subprocesses as "scripts"
+- command: implement asynchronous commands and support for named arguments
+- player: add optional separate video decoding thread
+- vo_gpu: d3d11: add support for exclusive fullscreen
+- w32_common: Support HiDPI on Windows
+- vo_x11: add 10 bit support
+- vo_gpu: add BT.2390 tone-mapping
+- client API: add software rendering API
+- audio: add scaletempo2 filter based on chromium
+- auto_profiles: add this script
+- stream: Implement slice:// for reading slices of streams
+- player: add automatic loading of external cover art files
+- vo_sixel: implement terminal video output using sixel
Changed
~~~~~~~
-- wayland: print warning on GNOME due to serious issues with their compositor
-- player: write watch-later config even for unseekable streams
+- sws_utils: use zimg by default if available
+- build: pick up Lua 5.2 by default (preferred over 5.1)
+- build: disable RPI vendor blob auto-detection in favor of open-source stack
+- build: make C11 atomics mandatory
+- build: make libass non-optional
+- player: stricter filename matching for external subtitle auto-loading
+- lua: support Unicode paths in script loading and IO library
+- vo_direct3d: rip out texture video rendering path and dumb down OSD rendering
+- audio: rewrite internal audio handling and AO API
+- build: disable GLX by default
Removed
~~~~~~~
-- vo_gpu: hwdec_vdpau: remove direct_mode
-- vo_gpu: hwdec_vaegl: remove support for old-style interop
+- stream_libarchive: disable tar support due to bugs
+- Remove remains of Libav compatibility
+- stream_smb: remove due to lack of thread safety and the abundance
+ of alternatives, FFmpeg still includes SMB support
+- command: remove legacy hook API (has been deprecated for a long time)
+- client API: remove deprecated qthelper.hpp header
+- removed audio outputs: sndio, rsound, oss
+- x11: remove xdg-screensaver invocations that supported dbus based idle inhibit
+- client API: deactivate the opengl_cb API
+- build system: drop Python 2 compatbility
Options and Commands
@@ -37,33 +69,90 @@ Options and Commands
Added
~~~~~
-- command: add a playlist-unshuffle command
-- command: add osd-dimensions property
-- input: new PLAYONLY and PAUSEONLY keycodes
+- demux: add option to disable cache "sharing" between back and forward buffers
+- player: add ab-loop-count option/property
+- ytdl_hook: add a way to use ytdl's default formats
+- ytdl_hook: add all_formats option that loads all formats that were found
+- demux_mkv: document probe-start-time option and enable it by default
+- command: extend osd-overlay command with bounds reporting
+- player: a number of new playlist contol commands/properties
+- ipc: add --input-ipc-client option
+- options: add option to control display-sync factor
+- vo_gpu: add better gamut clipping option
+- vo_gpu: vulkan: add ability to disable events
+- x11: add option to make window appear on a specific workspace
+- wayland: expose wayland-app-id as a user option
+- player: add --subs-with-matching-audio option
+- command: add read-only focused property
+- screenshot: option to use software rendering for screenshots
+- command: add delete-watch-later-config
+- command: new property mouse-pos with current position and hover state
Changed
~~~~~~~
-- options: change option parsing when using a single dash
- This adds a warning for `-o file.mkv` and disallows the use of
- `--o file.mkv` (use `--o=file.mkv` instead).
+- options: remove deprecation warning for "-foo bar" syntax
+- player: make audio hr-seek default
+- ad_lavc: disable decoder downmix by default
+- command: support save-position-on-quit for "stop" command too
+- command: extend subprocess command
+- options: do not accept ":" as separator anymore in key/value lists
+
+
+Deprecated
+~~~~~~~~~~
+
+- demux: deprecate --cache-secs
+
+
+Removed
+~~~~~~~
+
+- wayland: remove wayland-frame-wait-offset option
+- input: remove deprecated --input-file option
+- vo_vdpau: remove deprecated/inactive --vo-vdpau-deint option
Fixes and Minor Enhancements
----------------------------
-- lua: fix mp.file_info for large files
-- vo_gpu: fix crash if dither texture fails to allocate
-- wayland: unscrew up cursors, fix various issues
-- osc: usability improvements for osc window controls
+- options: stop hiding deprecated options from --help output
+- lua, js: add mp.get_script_directory() function
+- lua: fix security relevant loading order issue with scripts
+- player: make screenshot each-frame mode more accurate
+- bash completion: complete ao/af/vo/vf options
+- zimg: add alpha support
+- wayland: make resizing better
+- edl: add mechanism for delay loading streams
+- stream_file: fix caching-related performance regression on CIFS
+- cocoa-cb: fix crashes and issues with UI updates
+- sub, demux: improve behavior with negative subtitle delay/muxed subs
+- demux: make seek ranges work for static images + audio
+- cocoa-cb: support maximize/minimize on startup
+- umpv: change from legacy FIFO to socket
+- stats: move input speed to cache page, make it a graph
+- stats: add fourth page with performance graphs
+- command: print edition title to OSD when cycling
+- zimg: add support for big endian input and output
+- demux_mkv: add png intra support
+- build: detect VT_GETMODE on FreeBSD and DragonFly
+- win32: use windows 10 native virtual-terminal if available
+- vo_gpu: enable frame caching for still frames
+- command: add property to return text subtitles in ASS
+- vo_gpu: ra_pl: add timers support
+- build: allow vo_wlshm on more Wayland platforms (e.g. FreeBSD)
+- zimg: add slice threading and use it by default
+- command: add a way to access properties of a current track
+- vo_gpu: EGL: fix transparency on X11/EGL/Mesa
+- vd_lavc: add AV1 to the default allowed hwdec codec list
This listing is not complete. Check DOCS/client-api-changes.rst for a history
of changes to the client API, and DOCS/interface-changes.rst for a history
of changes to other user-visible interfaces.
-A complete changelog can be seen by running `git log v0.31.0..v0.32.0`
+A complete changelog can be seen by running `git log v0.32.0..v0.33.0`
in the git repository or by visiting either
-https://github.com/mpv-player/mpv/compare/v0.31.0...v0.32.0 or
-https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.31.0..v0.32.0
+https://github.com/mpv-player/mpv/compare/v0.32.0...v0.33.0 or
+https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.32.0..v0.33.0
diff --git a/TOOLS/appveyor-install.sh b/TOOLS/appveyor-install.sh
index 7b2473f39d..835850abf1 100755
--- a/TOOLS/appveyor-install.sh
+++ b/TOOLS/appveyor-install.sh
@@ -23,6 +23,7 @@ pacman -S --noconfirm --needed \
$MINGW_PACKAGE_PREFIX-lua51 \
$MINGW_PACKAGE_PREFIX-ninja \
$MINGW_PACKAGE_PREFIX-rubberband \
+ $MINGW_PACKAGE_PREFIX-shaderc \
$MINGW_PACKAGE_PREFIX-uchardet \
$MINGW_PACKAGE_PREFIX-vulkan
@@ -51,17 +52,6 @@ pacman -Sc --noconfirm
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
diff --git a/TOOLS/file2string.py b/TOOLS/file2string.py
index bdeb18ce86..b641a1df2f 100755
--- a/TOOLS/file2string.py
+++ b/TOOLS/file2string.py
@@ -22,26 +22,21 @@
# License along with mpv. If not, see <http://www.gnu.org/licenses/>.
#
-from __future__ import unicode_literals
import sys
-# Indexing a byte string yields int on Python 3.x, and a str on Python 2.x
-def pord(c):
- return ord(c) if type(c) == str else c
-
def file2string(infilename, infile, outfile):
outfile.write("// Generated from %s\n\n" % infilename)
- conv = ['\\' + ("%03o" % c) for c in range(256)]
+ conv = ["\\%03o" % c for c in range(256)]
safe_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" \
- "0123456789!#%&'()*+,-./:;<=>?[]^_{|}~ "
+ "0123456789!#%&'()*+,-./:;<=>[]^_{|}~ "
for c in safe_chars:
conv[ord(c)] = c
for c, esc in ("\nn", "\tt", r"\\", '""'):
conv[ord(c)] = '\\' + esc
for line in infile:
- outfile.write('"' + ''.join(conv[pord(c)] for c in line) + '"\n')
+ outfile.write('"' + ''.join(conv[c] for c in line) + '"\n')
if __name__ == "__main__":
with open(sys.argv[1], 'rb') as infile:
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/VERSION b/VERSION
index 1e0f8d9f52..17d545aad7 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.32.0-UNKNOWN
+0.33.0-UNKNOWN
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index 24f3355047..762805541f 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -915,7 +915,7 @@ static int init(struct ao *ao)
// Function for dealing with playback state. This attempts to recover the ALSA
// state (bring it into SND_PCM_STATE_{PREPARED,RUNNING,PAUSED,UNDERRUN}). If
-// state!=NULL, fill it after recovery.
+// state!=NULL, fill it after recovery is attempted.
// Returns true if PCM is in one the expected states.
static bool recover_and_get_state(struct ao *ao, struct mp_pcm_state *state)
{
@@ -933,9 +933,18 @@ static bool recover_and_get_state(struct ao *ao, struct mp_pcm_state *state)
// (where things were retried in a loop).
for (int n = 0; n < 10; n++) {
err = snd_pcm_status(p->alsa, st);
- CHECK_ALSA_ERROR("snd_pcm_status");
+ if (err == -EPIPE) {
+ // ALSA APIs can return -EPIPE when an XRUN happens,
+ // we skip right to handling it by setting pcmst
+ // manually.
+ pcmst = SND_PCM_STATE_XRUN;
+ } else {
+ // Otherwise do error checking and query the PCM state properly.
+ CHECK_ALSA_ERROR("snd_pcm_status");
+
+ pcmst = snd_pcm_status_get_state(st);
+ }
- pcmst = snd_pcm_status_get_state(st);
if (pcmst == SND_PCM_STATE_PREPARED ||
pcmst == SND_PCM_STATE_RUNNING ||
pcmst == SND_PCM_STATE_PAUSED)
@@ -983,19 +992,20 @@ static bool recover_and_get_state(struct ao *ao, struct mp_pcm_state *state)
ao_request_reload(ao);
p->device_lost = true;
}
- return false;
+ goto alsa_error;
}
}
if (!state_ok) {
MP_ERR(ao, "could not recover\n");
- return false;
}
+alsa_error:
+
if (state) {
- snd_pcm_sframes_t del = snd_pcm_status_get_delay(st);
+ snd_pcm_sframes_t del = state_ok ? snd_pcm_status_get_delay(st) : 0;
state->delay = MPMAX(del, 0) / (double)ao->samplerate;
- state->free_samples = snd_pcm_status_get_avail(st);
+ state->free_samples = state_ok ? snd_pcm_status_get_avail(st) : 0;
state->free_samples = MPCLAMP(state->free_samples, 0, ao->device_buffer);
// Align to period size.
state->free_samples = state->free_samples / p->outburst * p->outburst;
@@ -1004,10 +1014,7 @@ static bool recover_and_get_state(struct ao *ao, struct mp_pcm_state *state)
pcmst == SND_PCM_STATE_PAUSED;
}
- return true;
-
-alsa_error:
- return false;
+ return state_ok;
}
static void audio_get_state(struct ao *ao, struct mp_pcm_state *state)
diff --git a/bootstrap.py b/bootstrap.py
index a8bd67cb23..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))
@@ -41,6 +36,12 @@ if not waf:
sys.exit(1)
if SHA256HASH == hashlib.sha256(waf).hexdigest():
+ # Upstream waf is not changing the default interpreter during
+ # 2.0.x line due to compatibility reasons apparently. So manually
+ # convert it to use python3 (the script works with both).
+ expected = b"#!/usr/bin/env python\n"
+ assert waf.startswith(expected)
+ waf = b"#!/usr/bin/env python3\n" + waf[len(expected):]
with open("waf", "wb") as wf:
wf.write(waf)
diff --git a/ci/build-macos.sh b/ci/build-macos.sh
index 347a75160f..cf3fbb3509 100755
--- a/ci/build-macos.sh
+++ b/ci/build-macos.sh
@@ -19,7 +19,8 @@ PKG_CONFIG_PATH="${FFMPEG_SYSROOT}/lib/pkgconfig/" CC="${CC}" CXX="${CXX}" pytho
--variant="${MPV_VARIANT}" \
--prefix="${MPV_INSTALL_PREFIX}" \
--enable-{gl,iconv,lcms2,libmpv-shared,lua,jpeg,plain-gl,zlib} \
- --enable-{cocoa,coreaudio,gl-cocoa,macos-cocoa-cb,macos-touchbar,videotoolbox-gl}
+ --enable-{cocoa,coreaudio,gl-cocoa,macos-cocoa-cb,macos-touchbar,videotoolbox-gl} \
+ --swift-flags="${CI_SWIFT_FLAGS}"
python3 ./waf build --variant="${MPV_VARIANT}" -j4
diff --git a/ci/build-mingw64.sh b/ci/build-mingw64.sh
index 77a7c99cbb..574c5c3fd8 100755
--- a/ci/build-mingw64.sh
+++ b/ci/build-mingw64.sh
@@ -119,6 +119,17 @@ if [ ! -e "$prefix_dir/lib/libfribidi.dll.a" ]; then
popd
fi
+## harfbuzz
+if [ ! -e "$prefix_dir/lib/libharfbuzz.dll.a" ]; then
+ ver=2.7.2
+ gettar "https://github.com/harfbuzz/harfbuzz/releases/download/${ver}/harfbuzz-${ver}.tar.xz"
+ builddir harfbuzz-${ver}
+ ../configure --host=$TARGET $commonflags --with-icu=no
+ makeplusinstall
+ popd
+ rm "$prefix_dir"/lib/*.la # fuck off
+fi
+
## libass
if [ ! -e "$prefix_dir/lib/libass.dll.a" ]; then
[ -d libass ] || $gitclone https://github.com/libass/libass.git
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/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/input/input.c b/input/input.c
index 1d0c60569c..40abd7d0fa 100644
--- a/input/input.c
+++ b/input/input.c
@@ -122,6 +122,7 @@ struct input_ctx {
// Mouse position on the consumer side (as command.c sees it)
int mouse_x, mouse_y;
+ int mouse_hover; // updated on mouse-enter/leave
char *mouse_section; // last section to receive mouse event
// Mouse position on the producer side (as the VO sees it)
@@ -719,8 +720,13 @@ static void mp_input_feed_key(struct input_ctx *ictx, int code, double scale,
if (!opts->enable_mouse_movements && MP_KEY_IS_MOUSE(unmod) && !force_mouse)
return;
if (unmod == MP_KEY_MOUSE_LEAVE || unmod == MP_KEY_MOUSE_ENTER) {
+ ictx->mouse_hover = unmod == MP_KEY_MOUSE_ENTER;
update_mouse_section(ictx);
- mp_input_queue_cmd(ictx, get_cmd_from_keys(ictx, NULL, code));
+
+ mp_cmd_t *cmd = get_cmd_from_keys(ictx, NULL, code);
+ if (!cmd) // queue dummy cmd so that mouse-pos can notify observers
+ cmd = mp_input_parse_cmd(ictx, bstr0("ignore"), "<internal>");
+ mp_input_queue_cmd(ictx, cmd);
return;
}
double now = mp_time_sec();
@@ -962,11 +968,12 @@ mp_cmd_t *mp_input_read_cmd(struct input_ctx *ictx)
return ret;
}
-void mp_input_get_mouse_pos(struct input_ctx *ictx, int *x, int *y)
+void mp_input_get_mouse_pos(struct input_ctx *ictx, int *x, int *y, int *hover)
{
input_lock(ictx);
*x = ictx->mouse_x;
*y = ictx->mouse_y;
+ *hover = ictx->mouse_hover;
input_unlock(ictx);
}
diff --git a/input/input.h b/input/input.h
index 82e7adc503..5b5e7a99ce 100644
--- a/input/input.h
+++ b/input/input.h
@@ -99,7 +99,7 @@ void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y);
// Like mp_input_set_mouse_pos(), but ignore mouse disable option.
void mp_input_set_mouse_pos_artificial(struct input_ctx *ictx, int x, int y);
-void mp_input_get_mouse_pos(struct input_ctx *ictx, int *x, int *y);
+void mp_input_get_mouse_pos(struct input_ctx *ictx, int *x, int *y, int *hover);
// Return whether we want/accept mouse input.
bool mp_input_mouse_enabled(struct input_ctx *ictx);
diff --git a/options/options.c b/options/options.c
index c297200a45..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)},
@@ -287,7 +289,7 @@ const struct m_sub_options mp_subtitle_sub_opts = {
.sub_scale = 1,
.ass_vsfilter_aspect_compat = 1,
.ass_vsfilter_color_compat = 1,
- .ass_vsfilter_blur_compat = 0,
+ .ass_vsfilter_blur_compat = 1,
.ass_style_override = 1,
.ass_shaper = 1,
.use_embedded_fonts = 1,
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/options/path.c b/options/path.c
index 9c996ce664..504adb7c84 100644
--- a/options/path.c
+++ b/options/path.c
@@ -59,6 +59,7 @@ static const char *const config_dirs[] = {
"home",
"old_home",
"osxbundle",
+ "exe_dir",
"global",
};
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/path-win.c b/osdep/path-win.c
index 8f289ff83a..78e83c2222 100644
--- a/osdep/path-win.c
+++ b/osdep/path-win.c
@@ -90,7 +90,7 @@ const char *mp_get_platform_path_win(void *talloc_ctx, const char *type)
} else {
if (strcmp(type, "home") == 0)
return mp_get_win_app_dir(talloc_ctx);
- if (strcmp(type, "old_home") == 0)
+ if (strcmp(type, "exe_dir") == 0)
return mp_get_win_exe_dir(talloc_ctx);
// Not really true, but serves as a way to return a lowest-priority dir.
if (strcmp(type, "global") == 0)
diff --git a/osdep/terminal-dummy.c b/osdep/terminal-dummy.c
index 4a3787e0fa..4b33d786a8 100644
--- a/osdep/terminal-dummy.c
+++ b/osdep/terminal-dummy.c
@@ -21,6 +21,10 @@ void terminal_get_size(int *w, int *h)
{
}
+void terminal_get_size2(int *rows, int *cols, int *px_width, int *px_height)
+{
+}
+
void mp_write_console_ansi(void *wstream, char *buf)
{
}
diff --git a/osdep/terminal-unix.c b/osdep/terminal-unix.c
index 8e4d75f78b..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) {
@@ -493,6 +501,19 @@ void terminal_get_size(int *w, int *h)
*h = ws.ws_row;
}
+void terminal_get_size2(int *rows, int *cols, int *px_width, int *px_height)
+{
+ struct winsize ws;
+ if (ioctl(tty_in, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col
+ || !ws.ws_xpixel || !ws.ws_ypixel)
+ return;
+
+ *rows = ws.ws_row;
+ *cols = ws.ws_col;
+ *px_width = ws.ws_xpixel;
+ *px_height = ws.ws_ypixel;
+}
+
void terminal_init(void)
{
assert(!getch2_enabled);
diff --git a/osdep/terminal-win.c b/osdep/terminal-win.c
index 3672f3a763..dab3fabc80 100644
--- a/osdep/terminal-win.c
+++ b/osdep/terminal-win.c
@@ -106,6 +106,10 @@ void terminal_get_size(int *w, int *h)
}
}
+void terminal_get_size2(int *rows, int *cols, int *px_width, int *px_height)
+{
+}
+
static bool has_input_events(HANDLE h)
{
DWORD num_events;
diff --git a/osdep/terminal.h b/osdep/terminal.h
index ccf6a02c7c..db1e2a4dd2 100644
--- a/osdep/terminal.h
+++ b/osdep/terminal.h
@@ -40,6 +40,9 @@ bool terminal_in_background(void);
/* Get terminal-size in columns/rows. */
void terminal_get_size(int *w, int *h);
+/* Get terminal-size in columns/rows and width/height in pixels. */
+void terminal_get_size2(int *rows, int *cols, int *px_width, int *px_height);
+
// Windows only.
void mp_write_console_ansi(void *wstream, char *buf);
diff --git a/player/command.c b/player/command.c
index b098c645a3..098a11d24f 100644
--- a/player/command.c
+++ b/player/command.c
@@ -2621,6 +2621,34 @@ static int mp_property_osd_ass(void *ctx, struct m_property *prop,
return m_property_read_sub(props, action, arg);
}
+static int mp_property_mouse_pos(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+
+ switch (action) {
+ case M_PROPERTY_GET_TYPE:
+ *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE};
+ return M_PROPERTY_OK;
+
+ case M_PROPERTY_GET: {
+ struct mpv_node node;
+ int x, y, hover;
+ mp_input_get_mouse_pos(mpctx->input, &x, &y, &hover);
+
+ node_init(&node, MPV_FORMAT_NODE_MAP, NULL);
+ node_map_add_int64(&node, "x", x);
+ node_map_add_int64(&node, "y", y);
+ node_map_add_flag(&node, "hover", hover);
+ *(struct mpv_node *)arg = node;
+
+ return M_PROPERTY_OK;
+ }
+ }
+
+ return M_PROPERTY_NOT_IMPLEMENTED;
+}
+
/// Video fps (RO)
static int mp_property_fps(void *ctx, struct m_property *prop,
int action, void *arg)
@@ -3591,6 +3619,8 @@ static const struct m_property mp_properties_base[] = {
{"osd-sym-cc", mp_property_osd_sym},
{"osd-ass-cc", mp_property_osd_ass},
+ {"mouse-pos", mp_property_mouse_pos},
+
// Subs
{"sid", property_switch_track, .priv = (void *)(const int[]){0, STREAM_SUB}},
{"secondary-sid", property_switch_track,
@@ -3682,7 +3712,8 @@ static const char *const *const mp_event_property_change[] = {
"vo-delayed-frame-count", "mistimed-frame-count", "vsync-ratio",
"estimated-display-fps", "vsync-jitter", "sub-text", "audio-bitrate",
"video-bitrate", "sub-bitrate", "decoder-frame-drop-count",
- "frame-drop-count", "video-frame-info", "vf-metadata", "af-metadata"),
+ "frame-drop-count", "video-frame-info", "vf-metadata", "af-metadata",
+ "sub-start", "sub-end"),
E(MP_EVENT_DURATION_UPDATE, "duration"),
E(MPV_EVENT_VIDEO_RECONFIG, "video-out-params", "video-params",
"video-format", "video-codec", "video-bitrate", "dwidth", "dheight",
@@ -3709,6 +3740,7 @@ static const char *const *const mp_event_property_change[] = {
E(MP_EVENT_CHANGE_PLAYLIST, "playlist", "playlist-pos", "playlist-pos-1",
"playlist-count", "playlist/count", "playlist-current-pos",
"playlist-playing-pos"),
+ E(MP_EVENT_INPUT_PROCESSED, "mouse-pos"),
E(MP_EVENT_CORE_IDLE, "core-idle", "eof-reached"),
};
#undef E
@@ -5705,10 +5737,26 @@ static void cmd_mouse(void *p)
{
struct mp_cmd_ctx *cmd = p;
struct MPContext *mpctx = cmd->mpctx;
+ int pre_key = 0;
const int x = cmd->args[0].v.i, y = cmd->args[1].v.i;
int button = cmd->args[2].v.i;
+
+ if (mpctx->video_out && mpctx->video_out->config_ok) {
+ int oldx, oldy, oldhover;
+ mp_input_get_mouse_pos(mpctx->input, &oldx, &oldy, &oldhover);
+ struct mp_osd_res vo_res = osd_get_vo_res(mpctx->osd);
+
+ // TODO: VOs don't send outside positions. should we abort if outside?
+ int hover = x >= 0 && y >= 0 && x < vo_res.w && y < vo_res.h;
+
+ if (vo_res.w && vo_res.h && hover != oldhover)
+ pre_key = hover ? MP_KEY_MOUSE_ENTER : MP_KEY_MOUSE_LEAVE;
+ }
+
if (button == -1) {// no button
+ if (pre_key)
+ mp_input_put_key_artificial(mpctx->input, pre_key);
mp_input_set_mouse_pos_artificial(mpctx->input, x, y);
return;
}
@@ -5725,6 +5773,8 @@ static void cmd_mouse(void *p)
return;
}
button += dbc ? MP_MBTN_DBL_BASE : MP_MBTN_BASE;
+ if (pre_key)
+ mp_input_put_key_artificial(mpctx->input, pre_key);
mp_input_set_mouse_pos_artificial(mpctx->input, x, y);
mp_input_put_key_artificial(mpctx->input, button);
}
@@ -6475,6 +6525,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);
diff --git a/player/command.h b/player/command.h
index c47ed40f1d..b45e616497 100644
--- a/player/command.h
+++ b/player/command.h
@@ -102,6 +102,7 @@ enum {
MP_EVENT_CHANGE_PLAYLIST,
MP_EVENT_CORE_IDLE,
MP_EVENT_DURATION_UPDATE,
+ MP_EVENT_INPUT_PROCESSED,
};
bool mp_hook_test_completion(struct MPContext *mpctx, char *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/external_files.c b/player/external_files.c
index e42558f594..06585bf18f 100644
--- a/player/external_files.c
+++ b/player/external_files.c
@@ -43,6 +43,7 @@ static const char *const audio_exts[] = {"mp3", "aac", "mka", "dts", "flac",
NULL};
// Stolen from: vlc/-/blob/master/modules/meta_engine/folder.c#L40
+// sorted by priority (descending)
static const char *const cover_files[] = {
"AlbumArt.jpg",
"Album.jpg",
@@ -81,11 +82,13 @@ static int test_ext(bstr ext)
return -1;
}
-static int test_filename(bstr fname)
+static int test_cover_filename(bstr fname, int *priority)
{
for (int n = 0; cover_files[n]; n++) {
- if (bstrcasecmp(bstr0(cover_files[n]), fname) == 0)
+ if (bstrcasecmp(bstr0(cover_files[n]), fname) == 0) {
+ *priority = MP_ARRAY_SIZE(cover_files) - n;
return STREAM_VIDEO;
+ }
}
return -1;
}
@@ -188,9 +191,10 @@ static void append_dir_subtitles(struct mpv_global *global, struct MPOpts *opts,
talloc_steal(tmpmem2, dename.start);
// check what it is (most likely)
+ int cover_prio = 0;
int type = test_ext(tmp_fname_ext);
if (type < 0)
- type = test_filename(dename);
+ type = test_cover_filename(dename, &cover_prio);
char **langs = NULL;
int fuzz = -1;
switch (type) {
@@ -247,7 +251,7 @@ static void append_dir_subtitles(struct mpv_global *global, struct MPOpts *opts,
// cover art: just accept it
if (type == STREAM_VIDEO && fuzz >= 1)
- prio |= 1;
+ prio = cover_prio;
mp_dbg(log, "Potential external file: \"%s\" Priority: %d\n",
de->d_name, prio);
diff --git a/player/javascript.c b/player/javascript.c
index be28ef9e66..8cb263e123 100644
--- a/player/javascript.c
+++ b/player/javascript.c
@@ -33,6 +33,7 @@
#include "options/m_property.h"
#include "common/msg.h"
#include "common/msg_control.h"
+#include "common/stats.h"
#include "options/m_option.h"
#include "input/input.h"
#include "options/path.h"
@@ -64,6 +65,8 @@ struct script_ctx {
struct MPContext *mpctx;
struct mp_log *log;
char *last_error_str;
+ size_t js_malloc_size;
+ struct stats_ctx *stats;
};
static struct script_ctx *jctx(js_State *J)
@@ -458,6 +461,25 @@ static int s_init_js(js_State *J, struct script_ctx *ctx)
return 0;
}
+static void *mp_js_alloc(void *actx, void *ptr, int size_)
+{
+ if (size_ < 0)
+ return NULL;
+
+ struct script_ctx* ctx = actx;
+ size_t size = size_, osize = 0;
+ if (ptr) // free/realloc
+ osize = ta_get_size(ptr);
+
+ void *ret = talloc_realloc_size(actx, ptr, size);
+
+ if (!size || ret) { // free / successful realloc/malloc
+ ctx->js_malloc_size = ctx->js_malloc_size - osize + size;
+ stats_size_value(ctx->stats, "mem", ctx->js_malloc_size);
+ }
+ return ret;
+}
+
/**********************************************************************
* Initialization - booting the script
*********************************************************************/
@@ -479,10 +501,24 @@ static int s_load_javascript(struct mp_script_args *args)
.last_error_str = talloc_strdup(ctx, "Cannot initialize JavaScript"),
.filename = args->filename,
.path = args->path,
+ .js_malloc_size = 0,
+ .stats = stats_ctx_create(ctx, args->mpctx->global,
+ mp_tprintf(80, "script/%s", mpv_client_name(args->client))),
};
+ stats_register_thread_cputime(ctx->stats, "cpu");
+
+ js_Alloc alloc_fn = NULL;
+ void *actx = NULL;
+
+ char *mem_report = getenv("MPV_LEAK_REPORT");
+ if (mem_report && strcmp(mem_report, "1") == 0) {
+ alloc_fn = mp_js_alloc;
+ actx = ctx;
+ }
+
int r = -1;
- js_State *J = js_newstate(NULL, NULL, 0);
+ js_State *J = js_newstate(alloc_fn, actx, 0);
if (!J || s_init_js(J, ctx))
goto error_out;
@@ -745,16 +781,6 @@ static void push_nums_obj(js_State *J, const char * const names[],
}
}
-// args: none, return: object with properties x, y
-static void script_get_mouse_pos(js_State *J)
-{
- int x, y;
- mp_input_get_mouse_pos(jctx(J)->mpctx->input, &x, &y);
- const char * const names[] = {"x", "y", NULL};
- const double vals[] = {x, y};
- push_nums_obj(J, names, vals);
-}
-
// args: input-section-name, x0, y0, x1, y1
static void script_input_set_section_mouse_area(js_State *J)
{
@@ -1145,7 +1171,6 @@ static const struct fn_entry main_fns[] = {
FN_ENTRY(get_wakeup_pipe, 0),
FN_ENTRY(_hook_add, 3),
FN_ENTRY(_hook_continue, 1),
- FN_ENTRY(get_mouse_pos, 0),
FN_ENTRY(input_set_section_mouse_area, 5),
FN_ENTRY(last_error, 0),
FN_ENTRY(_set_last_error, 1),
diff --git a/player/javascript/defaults.js b/player/javascript/defaults.js
index 2435390b87..0904524ce4 100644
--- a/player/javascript/defaults.js
+++ b/player/javascript/defaults.js
@@ -663,6 +663,7 @@ mp.get_script_file = function() { return mp.script_file };
mp.get_script_directory = function() { return mp.script_path };
mp.get_time = function() { return mp.get_time_ms() / 1000 };
mp.utils.getcwd = function() { return mp.get_property("working-directory") };
+mp.get_mouse_pos = function() { return mp.get_property_native("mouse-pos") };
mp.dispatch_event = dispatch_event;
mp.process_timers = process_timers;
mp.notify_idle_observers = notify_idle_observers;
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.c b/player/lua.c
index 363e1a52fd..2eb163ef16 100644
--- a/player/lua.c
+++ b/player/lua.c
@@ -991,16 +991,6 @@ static int script_raw_abort_async_command(lua_State *L)
return 0;
}
-static int script_get_mouse_pos(lua_State *L)
-{
- struct MPContext *mpctx = get_mpctx(L);
- int px, py;
- mp_input_get_mouse_pos(mpctx->input, &px, &py);
- lua_pushnumber(L, px);
- lua_pushnumber(L, py);
- return 2;
-}
-
static int script_get_time(lua_State *L)
{
struct script_ctx *ctx = get_ctx(L);
@@ -1242,7 +1232,6 @@ static const struct fn_entry main_fns[] = {
AF_ENTRY(set_property_native),
FN_ENTRY(raw_observe_property),
FN_ENTRY(raw_unobserve_property),
- FN_ENTRY(get_mouse_pos),
FN_ENTRY(get_time),
FN_ENTRY(input_set_section_mouse_area),
FN_ENTRY(format_time),
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/player/lua/defaults.lua b/player/lua/defaults.lua
index 95f8952f5d..920ee425aa 100644
--- a/player/lua/defaults.lua
+++ b/player/lua/defaults.lua
@@ -42,6 +42,11 @@ function mp.input_disable_section(section)
mp.commandv("disable-section", section)
end
+function mp.get_mouse_pos()
+ local m = mp.get_property_native("mouse-pos")
+ return m.x, m.y
+end
+
-- For dispatching script-binding. This is sent as:
-- script-message-to $script_name $binding_name $keystate
-- The array is indexed by $binding_name, and has functions like this as value:
diff --git a/player/playloop.c b/player/playloop.c
index 6cff3c9108..87c8b6390c 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -110,13 +110,17 @@ void mp_core_unlock(struct MPContext *mpctx)
// Process any queued user input.
static void mp_process_input(struct MPContext *mpctx)
{
+ int processed = 0;
for (;;) {
mp_cmd_t *cmd = mp_input_read_cmd(mpctx->input);
if (!cmd)
break;
run_command(mpctx, cmd, NULL, NULL, NULL);
+ processed = 1;
}
mp_set_timeout(mpctx, mp_input_get_delay(mpctx->input));
+ if (processed)
+ mp_notify(mpctx, MP_EVENT_INPUT_PROCESSED, NULL);
}
double get_relative_time(struct MPContext *mpctx)
diff --git a/stream/stream.c b/stream/stream.c
index f422c5d08d..116dcef4a4 100644
--- a/stream/stream.c
+++ b/stream/stream.c
@@ -441,8 +441,7 @@ int stream_create_with_args(struct stream_open_args *args, struct stream **ret)
if (r == STREAM_UNSAFE) {
mp_err(log, "\nRefusing to load potentially unsafe URL from a playlist.\n"
- "Use --playlist=file or the --load-unsafe-playlists option to "
- "load it anyway.\n\n");
+ "Use the --load-unsafe-playlists option to load it anyway.\n\n");
} else if (r == STREAM_NO_MATCH || r == STREAM_UNSUPPORTED) {
mp_err(log, "No protocol handler found to open URL %s\n", args->url);
mp_err(log, "The protocol is either unsupported, or was disabled "
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 e0732397a8..15eab18bf9 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",
+ .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
diff --git a/video/out/cocoa_cb_common.swift b/video/out/cocoa_cb_common.swift
index 476b4820bc..8dd76f4bc5 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
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..2aae3171e6 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;
}
@@ -2341,26 +2346,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 +3254,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) {
diff --git a/video/out/gpu/video_shaders.c b/video/out/gpu/video_shaders.c
index da48929328..7073185eaf 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"
@@ -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/mac/common.swift b/video/out/mac/common.swift
index 7b3c0fa10d..6c3d0fcfb3 100644
--- a/video/out/mac/common.swift
+++ b/video/out/mac/common.swift
@@ -38,6 +38,8 @@ class Common: NSObject {
var displaySleepAssertion: IOPMAssertionID = IOPMAssertionID(0)
+ var appNotificationObservers: [NSObjectProtocol] = []
+
var cursorVisibilityWanted: Bool = true
var title: String = "mpv" {
@@ -57,6 +59,7 @@ class Common: NSObject {
startDisplayLink(vo)
initLightSensor()
addDisplayReconfigureObserver()
+ addAppNotifications()
mpv.setMacOptionCallback(macOptsWakeupCallback, context: self)
}
@@ -152,6 +155,7 @@ class Common: NSObject {
stopDisplaylink()
uninitLightSensor()
removeDisplayReconfigureObserver()
+ removeAppNotifications()
enableDisplaySleep()
window?.orderOut(nil)
@@ -182,8 +186,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")
@@ -347,6 +350,34 @@ class Common: NSObject {
CGDisplayRemoveReconfigurationCallback(reconfigureCallback, MPVHelper.bridge(obj: self))
}
+ func addAppNotifications() {
+ appNotificationObservers.append(NotificationCenter.default.addObserver(
+ forName: NSApplication.didBecomeActiveNotification,
+ object: nil,
+ queue: .main,
+ using: { [weak self] (_) in self?.appDidBecomeActive() }
+ ))
+ appNotificationObservers.append(NotificationCenter.default.addObserver(
+ forName: NSApplication.didResignActiveNotification,
+ object: nil,
+ queue: .main,
+ using: { [weak self] (_) in self?.appDidResignActive() }
+ ))
+ }
+
+ func removeAppNotifications() {
+ appNotificationObservers.forEach { NotificationCenter.default.removeObserver($0) }
+ appNotificationObservers.removeAll()
+ }
+
+ func appDidBecomeActive() {
+ flagEvents(VO_EVENT_FOCUS)
+ }
+
+ func appDidResignActive() {
+ flagEvents(VO_EVENT_FOCUS)
+ }
+
func setAppIcon() {
if let app = NSApp as? Application,
ProcessInfo.processInfo.environment["MPVBUNDLE"] != "true"
@@ -377,9 +408,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? {
@@ -391,21 +440,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)
}
@@ -414,7 +469,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)
}
@@ -476,42 +531,36 @@ 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.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
@@ -621,19 +670,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..a418f2ce75 100644
--- a/video/out/mac/window.swift
+++ b/video/out/mac/window.swift
@@ -163,22 +163,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) {
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index 95ea9f65e0..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;
@@ -128,8 +55,6 @@ static void resize(struct ra_ctx *ctx)
if (p->egl_window)
wl_egl_window_resize(p->egl_window, width, height, 0, 0);
- if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
- wl_surface_commit(wl->surface);
wl->vo->dwidth = width;
wl->vo->dheight = height;
}
@@ -211,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..8244acff26 100644
--- a/video/out/placebo/ra_pl.c
+++ b/video/out/placebo/ra_pl.c
@@ -144,8 +144,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,
};
@@ -195,10 +201,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,
});
@@ -399,7 +407,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 +646,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;
diff --git a/video/out/vo.c b/video/out/vo.c
index 27be4735ab..4cb15123ab 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -64,6 +64,7 @@ extern const struct vo_driver video_out_vaapi;
extern const struct vo_driver video_out_wlshm;
extern const struct vo_driver video_out_rpi;
extern const struct vo_driver video_out_tct;
+extern const struct vo_driver video_out_sixel;
const struct vo_driver *const video_out_drivers[] =
{
@@ -106,6 +107,9 @@ const struct vo_driver *const video_out_drivers[] =
#if HAVE_RPI_MMAL
&video_out_rpi,
#endif
+#if HAVE_SIXEL
+ &video_out_sixel,
+#endif
&video_out_lavc,
NULL
};
diff --git a/video/out/vo_sixel.c b/video/out/vo_sixel.c
new file mode 100644
index 0000000000..c9cc1573cb
--- /dev/null
+++ b/video/out/vo_sixel.c
@@ -0,0 +1,580 @@
+/*
+ * Sixel mpv output device implementation based on ffmpeg libavdevice implementation
+ * by Hayaki Saito
+ * https://github.com/saitoha/FFmpeg-SIXEL/blob/sixel/libavdevice/sixel.c
+ *
+ * Copyright (c) 2014 Hayaki Saito
+ *
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <libswscale/swscale.h>
+#include <sixel.h>
+
+#include "config.h"
+#include "options/m_config.h"
+#include "osdep/terminal.h"
+#include "sub/osd.h"
+#include "vo.h"
+#include "video/sws_utils.h"
+#include "video/mp_image.h"
+
+#define IMGFMT IMGFMT_RGB24
+
+#define TERMINAL_FALLBACK_COLS 80
+#define TERMINAL_FALLBACK_ROWS 25
+#define TERMINAL_FALLBACK_PX_WIDTH 320
+#define TERMINAL_FALLBACK_PX_HEIGHT 240
+
+#define ESC_HIDE_CURSOR "\033[?25l"
+#define ESC_RESTORE_CURSOR "\033[?25h"
+#define ESC_CLEAR_SCREEN "\033[2J"
+#define ESC_GOTOXY "\033[%d;%df"
+#define ESC_USE_GLOBAL_COLOR_REG "\033[?1070l"
+
+struct priv {
+
+ // User specified options
+ int opt_diffuse;
+ int opt_width;
+ int opt_height;
+ int opt_reqcolors;
+ int opt_fixedpal;
+ int opt_threshold;
+ int opt_top;
+ int opt_left;
+ int opt_pad_y;
+ 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;
+
+ 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;
+
+ struct mp_rect src_rect;
+ struct mp_rect dst_rect;
+ struct mp_osd_res osd;
+ struct mp_image *frame;
+ struct mp_sws_context *sws;
+};
+
+static const unsigned int depth = 3;
+
+static int detect_scene_change(struct vo* vo)
+{
+ struct priv* priv = vo->priv;
+ int previous_histgram_colors = priv->previous_histgram_colors;
+ int histgram_colors = 0;
+
+ // If threshold is set negative, then every frame must be a scene change
+ if (priv->dither == NULL || priv->opt_threshold < 0)
+ return 1;
+
+ histgram_colors = sixel_dither_get_num_of_histogram_colors(priv->testdither);
+
+ int color_difference_count = previous_histgram_colors - histgram_colors;
+ color_difference_count = (color_difference_count > 0) ? // abs value
+ color_difference_count : -color_difference_count;
+
+ if (100 * color_difference_count >
+ priv->opt_threshold * previous_histgram_colors)
+ {
+ priv->previous_histgram_colors = histgram_colors; // update history
+ return 1;
+ } else {
+ return 0;
+ }
+
+}
+
+static void dealloc_dithers_and_buffers(struct vo* vo)
+{
+ struct priv* priv = vo->priv;
+
+ if (priv->buffer) {
+ talloc_free(priv->buffer);
+ priv->buffer = NULL;
+ }
+
+ if (priv->frame) {
+ talloc_free(priv->frame);
+ priv->frame = NULL;
+ }
+
+ if (priv->dither) {
+ sixel_dither_unref(priv->dither);
+ priv->dither = NULL;
+ }
+
+ if (priv->testdither) {
+ sixel_dither_unref(priv->testdither);
+ priv->testdither = NULL;
+ }
+}
+
+static SIXELSTATUS prepare_static_palette(struct vo* vo)
+{
+ struct priv* priv = vo->priv;
+
+ 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;
+}
+
+static SIXELSTATUS prepare_dynamic_palette(struct vo *vo)
+{
+ SIXELSTATUS status = SIXEL_FALSE;
+ struct priv *priv = vo->priv;
+
+ /* create histgram and construct color palette
+ * with median cut algorithm. */
+ status = sixel_dither_initialize(priv->testdither, priv->buffer,
+ priv->width, priv->height,
+ SIXEL_PIXELFORMAT_RGB888,
+ LARGE_NORM, REP_CENTER_BOX,
+ QUALITY_LOW);
+ if (SIXEL_FAILED(status))
+ return status;
+
+ if (detect_scene_change(vo)) {
+ if (priv->dither) {
+ sixel_dither_unref(priv->dither);
+ priv->dither = NULL;
+ }
+
+ priv->dither = priv->testdither;
+ status = sixel_dither_new(&priv->testdither, priv->opt_reqcolors, NULL);
+
+ if (SIXEL_FAILED(status))
+ return status;
+
+ sixel_dither_set_diffusion_type(priv->dither, priv->opt_diffuse);
+ } else {
+ if (priv->dither == NULL)
+ return SIXEL_FALSE;
+ }
+
+ sixel_dither_set_body_only(priv->dither, 0);
+ return status;
+}
+
+static void update_canvas_dimensions(struct vo *vo)
+{
+ // this function sets the vo canvas size in pixels vo->dwidth, vo->dheight,
+ // 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;
+ int total_px_width = 0;
+ int total_px_height = 0;
+
+ terminal_get_size2(&num_rows, &num_cols, &total_px_width, &total_px_height);
+
+ // If the user has specified rows/cols use them for further calculations
+ num_rows = (priv->opt_rows > 0) ? priv->opt_rows : num_rows;
+ num_cols = (priv->opt_cols > 0) ? priv->opt_cols : num_cols;
+
+ // If the pad value is set in between 0 and width/2 - 1, then we
+ // subtract from the detected width. Otherwise, we assume that the width
+ // output must be a integer multiple of num_cols and accordingly set
+ // total_width to be an integer multiple of num_cols. So in case the padding
+ // added by terminal is less than the number of cells in that axis, then rounding
+ // down will take care of correcting the detected width and remove padding.
+ if (priv->opt_width > 0) {
+ // option - set by the user, hard truth
+ total_px_width = priv->opt_width;
+ } else {
+ if (total_px_width <= 0) {
+ // ioctl failed to read terminal width
+ total_px_width = TERMINAL_FALLBACK_PX_WIDTH;
+ } else {
+ if (priv->opt_pad_x >= 0 && priv->opt_pad_x < total_px_width / 2) {
+ // explicit padding set by the user
+ total_px_width -= (2 * priv->opt_pad_x);
+ } else {
+ // rounded "auto padding"
+ total_px_width = total_px_width / num_cols * num_cols;
+ }
+ }
+ }
+
+ if (priv->opt_height > 0) {
+ total_px_height = priv->opt_height;
+ } else {
+ if (total_px_height <= 0) {
+ total_px_height = TERMINAL_FALLBACK_PX_HEIGHT;
+ } else {
+ if (priv->opt_pad_y >= 0 && priv->opt_pad_y < total_px_height / 2) {
+ total_px_height -= (2 * priv->opt_pad_y);
+ } else {
+ total_px_height = total_px_height / num_rows * num_rows;
+ }
+ }
+ }
+
+ // use n-1 rows for height
+ // The last row can't be used for encoding image, because after sixel encode
+ // the terminal moves the cursor to next line below the image, causing the
+ // last line to be empty instead of displaying image data.
+ // TODO: Confirm if the output height must be a multiple of 6, if not, remove
+ // the / 6 * 6 part which is setting the height to be a multiple of 6.
+ 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
+ // and they are not changed anywhere else outside this function.
+ // It is the sixel image output dimension which is output by libsixel.
+ priv->width = priv->dst_rect.x1 - priv->dst_rect.x0;
+ priv->height = priv->dst_rect.y1 - priv->dst_rect.y0;
+
+ // 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 :
+ priv->num_rows * priv->dst_rect.y0 / vo->dheight + 1;
+ priv->left = (priv->opt_left > 0) ? priv->opt_left :
+ priv->num_cols * priv->dst_rect.x0 / vo->dwidth + 1;
+}
+
+static int update_sixel_swscaler(struct vo *vo, struct mp_image_params *params)
+{
+ struct priv *priv = vo->priv;
+
+ priv->sws->src = *params;
+ priv->sws->src.w = mp_rect_w(priv->src_rect);
+ priv->sws->src.h = mp_rect_h(priv->src_rect);
+ priv->sws->dst = (struct mp_image_params) {
+ .imgfmt = IMGFMT,
+ .w = priv->width,
+ .h = priv->height,
+ .p_w = 1,
+ .p_h = 1,
+ };
+
+ dealloc_dithers_and_buffers(vo);
+
+ priv->frame = mp_image_alloc(IMGFMT, priv->width, priv->height);
+ if (!priv->frame)
+ return -1;
+
+ if (mp_sws_reinit(priv->sws) < 0)
+ 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 =
+ talloc_array(NULL, uint8_t, depth * priv->width * priv->height);
+
+ return 0;
+}
+
+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;
+ 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;
+
+ 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);
+
+ 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]);
+
+ // 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
+ // any NULL pointers. flip_page also has a check to make sure dither is not
+ // NULL before drawing, so failure in these functions should still be okay.
+ if (priv->opt_fixedpal) {
+ status = prepare_static_palette(vo);
+ } else {
+ status = prepare_dynamic_palette(vo);
+ }
+
+ if (SIXEL_FAILED(status)) {
+ MP_WARN(vo, "draw_frame: prepare_palette returned error: %s\n",
+ sixel_helper_format_error(status));
+ }
+
+ if (mpi)
+ talloc_free(mpi);
+}
+
+static int sixel_write(char *data, int size, void *priv)
+{
+ return fwrite(data, 1, size, (FILE *)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)
+ return;
+
+ // 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,
+ depth, priv->dither, priv->output);
+ fflush(stdout);
+}
+
+static int preinit(struct vo *vo)
+{
+ struct priv *priv = vo->priv;
+ SIXELSTATUS status = SIXEL_FALSE;
+ FILE* sixel_output_file = stdout;
+
+ // Parse opts set by CLI or conf
+ priv->sws = mp_sws_alloc(vo);
+ priv->sws->log = vo->log;
+ mp_sws_enable_cmdline_opts(priv->sws, vo->global);
+
+ status = sixel_output_new(&priv->output, sixel_write, sixel_output_file, NULL);
+ if (SIXEL_FAILED(status)) {
+ MP_ERR(vo, "preinit: Failed to create output file: %s\n",
+ sixel_helper_format_error(status));
+ return -1;
+ }
+
+ sixel_output_set_encode_policy(priv->output, SIXEL_ENCODEPOLICY_FAST);
+
+ printf(ESC_HIDE_CURSOR);
+
+ /* don't use private color registers for each frame. */
+ printf(ESC_USE_GLOBAL_COLOR_REG);
+
+ priv->dither = NULL;
+
+ // 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;
+ }
+ }
+
+ priv->previous_histgram_colors = 0;
+
+ return 0;
+}
+
+static int query_format(struct vo *vo, int format)
+{
+ return format == IMGFMT;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ if (request == VOCTRL_SET_PANSCAN)
+ return (vo->config_ok && !reconfig(vo, vo->params)) ? VO_TRUE : VO_FALSE;
+ return VO_NOTIMPL;
+}
+
+
+static void uninit(struct vo *vo)
+{
+ struct priv *priv = vo->priv;
+
+ printf(ESC_RESTORE_CURSOR);
+
+ if (priv->opt_clear) {
+ printf(ESC_CLEAR_SCREEN);
+ printf(ESC_GOTOXY, 1, 1);
+ }
+ fflush(stdout);
+
+ if (priv->output) {
+ sixel_output_unref(priv->output);
+ priv->output = NULL;
+ }
+
+ dealloc_dithers_and_buffers(vo);
+}
+
+#define OPT_BASE_STRUCT struct priv
+
+const struct vo_driver video_out_sixel = {
+ .name = "sixel",
+ .description = "terminal graphics using sixels",
+ .preinit = preinit,
+ .query_format = query_format,
+ .reconfig = reconfig,
+ .control = control,
+ .draw_frame = draw_frame,
+ .flip_page = flip_page,
+ .uninit = uninit,
+ .priv_size = sizeof(struct priv),
+ .priv_defaults = &(const struct priv) {
+ .opt_diffuse = DIFFUSE_AUTO,
+ .opt_width = 0,
+ .opt_height = 0,
+ .opt_reqcolors = 256,
+ .opt_threshold = -1,
+ .opt_fixedpal = 1,
+ .opt_top = 0,
+ .opt_left = 0,
+ .opt_pad_y = -1,
+ .opt_pad_x = -1,
+ .opt_rows = 0,
+ .opt_cols = 0,
+ .opt_clear = 1,
+ },
+ .options = (const m_option_t[]) {
+ {"dither", OPT_CHOICE(opt_diffuse,
+ {"auto", DIFFUSE_AUTO},
+ {"none", DIFFUSE_NONE},
+ {"atkinson", DIFFUSE_ATKINSON},
+ {"fs", DIFFUSE_FS},
+ {"jajuni", DIFFUSE_JAJUNI},
+ {"stucki", DIFFUSE_STUCKI},
+ {"burkes", DIFFUSE_BURKES},
+ {"arithmetic", DIFFUSE_A_DITHER},
+ {"xor", DIFFUSE_X_DITHER})},
+ {"width", OPT_INT(opt_width)},
+ {"height", OPT_INT(opt_height)},
+ {"reqcolors", OPT_INT(opt_reqcolors)},
+ {"fixedpalette", OPT_FLAG(opt_fixedpal)},
+ {"threshold", OPT_INT(opt_threshold)},
+ {"top", OPT_INT(opt_top)},
+ {"left", OPT_INT(opt_left)},
+ {"pad-y", OPT_INT(opt_pad_y)},
+ {"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 dbfa386789..8319475f72 100644
--- a/video/out/vo_wlshm.c
+++ b/video/out/vo_wlshm.c
@@ -73,25 +73,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 +123,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);
@@ -219,10 +196,7 @@ static int resize(struct vo *vo)
p->free_buffers = buf->next;
talloc_free(buf);
}
- int ret = mp_sws_reinit(p->sws);
- if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
- wl_surface_commit(wl->surface);
- return ret;
+ return mp_sws_reinit(p->sws);
}
static int control(struct vo *vo, uint32_t request, void *data)
@@ -301,6 +275,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)
@@ -330,6 +317,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 7e6beb3bdd..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:
@@ -210,10 +134,7 @@ static bool resize(struct ra_ctx *ctx)
vo_wayland_set_opaque_region(wl, ctx->opts.want_alpha);
wl_surface_set_buffer_scale(wl->surface, wl->scaling);
- bool ok = ra_vk_ctx_resize(ctx, width, height);
- if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
- wl_surface_commit(wl->surface);
- return ok;
+ return ra_vk_ctx_resize(ctx, width, height);
}
static bool wayland_vk_reconfig(struct ra_ctx *ctx)
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index b72cd9346b..fe9c0b531c 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -125,6 +125,34 @@ static int set_cursor_visibility(struct vo_wayland_state *wl, bool on)
return VO_TRUE;
}
+static int get_mods(struct vo_wayland_state *wl)
+{
+ static char* const mod_names[] = {
+ XKB_MOD_NAME_SHIFT,
+ XKB_MOD_NAME_CTRL,
+ XKB_MOD_NAME_ALT,
+ XKB_MOD_NAME_LOGO,
+ };
+
+ static const int mods[] = {
+ MP_KEY_MODIFIER_SHIFT,
+ MP_KEY_MODIFIER_CTRL,
+ MP_KEY_MODIFIER_ALT,
+ 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))
+ modifiers |= mods[n];
+ }
+ return modifiers;
+}
+
static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t sx, wl_fixed_t sy)
@@ -207,6 +235,7 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
uint32_t state)
{
struct vo_wayland_state *wl = data;
+ int mpmod = 0;
state = state == WL_POINTER_BUTTON_STATE_PRESSED ? MP_KEY_STATE_DOWN
: MP_KEY_STATE_UP;
@@ -232,9 +261,11 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
break;
}
- if (button) {
- mp_input_put_key(wl->vo->input_ctx, button | state);
- }
+ if (wl->keyboard)
+ mpmod = get_mods(wl);
+
+ if (button)
+ 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) &&
(button == MP_MBTN_LEFT) && (state == MP_KEY_STATE_DOWN)) {
@@ -492,43 +523,20 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
{
struct vo_wayland_state *wl = data;
- uint32_t code = code = key + 8;
- xkb_keysym_t sym = xkb_state_key_get_one_sym(wl->xkb_state, code);
+ wl->keyboard_code = key + 8;
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(wl->xkb_state, wl->keyboard_code);
- int mpmod = state == WL_KEYBOARD_KEY_STATE_PRESSED ? MP_KEY_STATE_DOWN
- : MP_KEY_STATE_UP;
-
- static const char *mod_names[] = {
- XKB_MOD_NAME_SHIFT,
- XKB_MOD_NAME_CTRL,
- XKB_MOD_NAME_ALT,
- XKB_MOD_NAME_LOGO,
- 0,
- };
-
- static int mods[] = {
- MP_KEY_MODIFIER_SHIFT,
- MP_KEY_MODIFIER_CTRL,
- MP_KEY_MODIFIER_ALT,
- MP_KEY_MODIFIER_META,
- 0,
- };
-
- for (int n = 0; mods[n]; 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, code, index)
- && xkb_state_mod_index_is_active(wl->xkb_state, index,
- XKB_STATE_MODS_DEPRESSED))
- mpmod |= mods[n];
- }
+ state = state == WL_KEYBOARD_KEY_STATE_PRESSED ? MP_KEY_STATE_DOWN
+ : MP_KEY_STATE_UP;
+ int mpmod = get_mods(wl);
int mpkey = lookupkey(sym);
if (mpkey) {
- mp_input_put_key(wl->vo->input_ctx, mpkey | mpmod);
+ mp_input_put_key(wl->vo->input_ctx, mpkey | state | mpmod);
} else {
char s[128];
if (xkb_keysym_to_utf8(sym, s, sizeof(s)) > 0)
- mp_input_put_key_utf8(wl->vo->input_ctx, mpmod, bstr0(s));
+ mp_input_put_key_utf8(wl->vo->input_ctx, state | mpmod, bstr0(s));
}
}
@@ -677,7 +685,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)
@@ -786,10 +793,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;
@@ -801,9 +860,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);
@@ -844,6 +919,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)
{
@@ -1206,6 +1354,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;
@@ -1314,27 +1465,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)
@@ -1342,11 +1498,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);
}
@@ -1372,25 +1529,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;
@@ -1399,33 +1537,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);
-
- greatest_common_divisor(wl, vo->dwidth, vo->dheight);
- wl->reduced_width = vo->dwidth / wl->gcd;
- wl->reduced_height = vo->dheight / wl->gcd;
+ set_geometry(wl);
- 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);
@@ -1436,18 +1565,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;
@@ -1562,6 +1685,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 f2c1a31f66..70e2fcbe68 100644
--- a/video/out/wayland_common.h
+++ b/video/out/wayland_common.h
@@ -119,6 +119,7 @@ struct vo_wayland_state {
int64_t vsync_duration;
/* Input */
+ uint32_t keyboard_code;
struct wl_seat *seat;
struct wl_pointer *pointer;
struct wl_touch *touch;
@@ -148,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/waftools/detections/compiler_swift.py b/waftools/detections/compiler_swift.py
index ff11818702..be66df0fbe 100644
--- a/waftools/detections/compiler_swift.py
+++ b/waftools/detections/compiler_swift.py
@@ -17,7 +17,6 @@ def __add_swift_flags(ctx):
ctx.env.SWIFT_FLAGS = [
"-frontend", "-c", "-sdk", ctx.env.MACOS_SDK,
"-enable-objc-interop", "-emit-objc-header", "-parse-as-library",
- "-target", "x86_64-apple-macosx10.10"
]
verRe = re.compile("(?i)version\s?([\d.]+)")
@@ -150,7 +149,7 @@ def __find_macos_sdk(ctx):
# convert build version to a version string
# first 2 two digits are the major version, starting with 15 which is 10.11 (offset of 4)
# 1 char is the minor version, A => 0, B => 1 and ongoing
- # las digits are bugfix version, which are nor relevant for us
+ # last digits are bugfix version, which are not relevant for us
# eg 16E185 => 10.12.4, 17A360 => 10.13, 18B71 => 10.14.1
if sdk_build_version and isinstance(sdk_build_version, str):
verRe = re.compile("(\d+)(\D+)(\d+)")
@@ -158,6 +157,9 @@ def __find_macos_sdk(ctx):
major = int(version_parts.group(1)) - 4
minor = string.ascii_lowercase.index(version_parts.group(2).lower())
build_version = '10.' + str(major) + '.' + str(minor)
+ # from 20 onwards macOS 11.0 starts
+ if int(version_parts.group(1)) >= 20:
+ build_version = '11.' + str(minor)
if not isinstance(sdk_version, str):
sdk_version = '10.10.0'
diff --git a/waftools/generators/sources.py b/waftools/generators/sources.py
index 0f5b993758..dae1985e2d 100644
--- a/waftools/generators/sources.py
+++ b/waftools/generators/sources.py
@@ -34,7 +34,7 @@ def f2s(self):
def fn(out):
source = getattr(self, 'source', None)
src = self.path.find_resource(source)
- file2string(source, iter(src.read().splitlines(True)), out)
+ file2string(source, iter(src.read('rb').splitlines(True)), out)
execf(self, fn)
@TaskGen.feature('ebml_header')
diff --git a/wscript b/wscript
index 3d9430baf9..86231b79b1 100644
--- a/wscript
+++ b/wscript
@@ -182,7 +182,7 @@ main_dependencies = [
'name': '--swift',
'desc': 'macOS Swift build tools',
'deps': 'os-darwin',
- 'func': check_swift,
+ '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.'
}, {
@@ -741,6 +741,10 @@ video_output_features = [
'desc': 'EGL helper functions',
'deps': 'egl || rpi || egl-angle-win32 || egl-android',
'func': check_true
+ }, {
+ 'name': '--sixel',
+ 'desc': 'Sixel',
+ 'func': check_pkg_config('libsixel', '>= 1.5'),
}
]
@@ -954,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)
@@ -961,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))
diff --git a/wscript_build.py b/wscript_build.py
index 4de6bdbf27..14c254e1ec 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -497,6 +497,7 @@ def build(ctx):
( "video/out/vo_null.c" ),
( "video/out/vo_rpi.c", "rpi-mmal" ),
( "video/out/vo_sdl.c", "sdl2-video" ),
+ ( "video/out/vo_sixel.c", "sixel" ),
( "video/out/vo_tct.c" ),
( "video/out/vo_vaapi.c", "vaapi-x11 && gpl" ),
( "video/out/vo_vdpau.c", "vdpau" ),