Skip to content

Commit

Permalink
positioning.lua: add this script
Browse files Browse the repository at this point in the history
This script provides script messages and bindings to pan videos and
images, making mpv a better image viewer out of the box.
  • Loading branch information
guidocella committed Nov 16, 2024
1 parent 1c3f092 commit f0b043f
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 2 deletions.
2 changes: 2 additions & 0 deletions DOCS/man/mpv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1461,6 +1461,8 @@ works like in older mpv releases:

.. include:: console.rst

.. include:: positioning.rst

.. include:: lua.rst

.. include:: javascript.rst
Expand Down
4 changes: 4 additions & 0 deletions DOCS/man/options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,10 @@ Program Behavior
Enable the builtin script that lets you select from lists of items (default:
yes). By default, its keybindings start with the ``g`` key.

``--load-positioning=<yes|no>``
Enable the builtin script that provides various keybindings to pan videos
and images (default: yes).

``--player-operation-mode=<cplayer|pseudo-gui>``
For enabling "pseudo GUI mode", which means that the defaults for some
options are changed. This option should not normally be used directly, but
Expand Down
36 changes: 36 additions & 0 deletions DOCS/man/positioning.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
POSITIONING
===========

This script provides key bindings to pan videos and images.

Script messages and bindings
----------------------------

``pan <axis> <amount>``
Adjust ``--video-align-x`` or ``--video-align-y`` relatively to the OSD
sizes, rather than relatively to the video sizes like the options. This is
useful to pan large images consistently.

``axis`` can be ``x`` or ``y``, and ``amount`` is a number such that an
amount of 1 scrolls as much as the OSD width or height.

``drag-to-pan``
Pan the video while holding a mouse button, relatively to the clicked point in
the OSD.

``align-to-cursor``
Pan the video while holding a mouse button, relatively to the whole video.

``cursor-centric-zoom <zoom>``
Increase ``--video-zoom`` by ``zoom`` and adjust ``--video-align-x`` and
``--video-align-y`` to shift the OSD towards the position hovered by the
cursor, or the average position of touch points if known.

Configurable Options
~~~~~~~~~~~~~~~~~~~~

``suppress_osd``
Default: no

Whether to not print the new value of ``--video-zoom`` when using
``cursor-centric-zoom``.
2 changes: 2 additions & 0 deletions options/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ static const m_option_t mp_opts[] = {
OPT_CHOICE(lua_load_auto_profiles, {"no", 0}, {"yes", 1}, {"auto", -1}),
.flags = UPDATE_BUILTIN_SCRIPTS},
{"load-select", OPT_BOOL(lua_load_select), .flags = UPDATE_BUILTIN_SCRIPTS},
{"load-positioning", OPT_BOOL(lua_load_positioning), .flags = UPDATE_BUILTIN_SCRIPTS},
#endif

// ------------------------- stream options --------------------
Expand Down Expand Up @@ -1009,6 +1010,7 @@ static const struct MPOpts mp_default_opts = {
.lua_load_console = true,
.lua_load_auto_profiles = -1,
.lua_load_select = true,
.lua_load_positioning = true,
#endif
.auto_load_scripts = true,
.loop_times = 1,
Expand Down
1 change: 1 addition & 0 deletions options/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ typedef struct MPOpts {
bool lua_load_console;
int lua_load_auto_profiles;
bool lua_load_select;
bool lua_load_positioning;

bool auto_load_scripts;

Expand Down
2 changes: 1 addition & 1 deletion player/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ typedef struct MPContext {

struct mp_ipc_ctx *ipc_ctx;

int64_t builtin_script_ids[6];
int64_t builtin_script_ids[7];

mp_mutex abort_lock;

Expand Down
3 changes: 3 additions & 0 deletions player/lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ static const char * const builtin_lua_scripts[][2] = {
},
{"@select.lua",
# include "player/lua/select.lua.inc"
},
{"@positioning.lua",
# include "player/lua/positioning.lua.inc"
},
{0}
};
Expand Down
2 changes: 1 addition & 1 deletion player/lua/meson.build
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
lua_files = ['defaults.lua', 'assdraw.lua', 'options.lua', 'osc.lua',
'ytdl_hook.lua', 'stats.lua', 'console.lua', 'auto_profiles.lua',
'input.lua', 'fzy.lua', 'select.lua']
'input.lua', 'fzy.lua', 'select.lua', 'positioning.lua']
foreach file: lua_files
lua_file = custom_target(file,
input: file,
Expand Down
119 changes: 119 additions & 0 deletions player/lua/positioning.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
--[[
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/>.
]]

local options = {
suppress_osd = false,
}

require "mp.options".read_options(options)

mp.register_script_message("pan", function (axis, amount)
local dims = mp.get_property_native("osd-dimensions")
local dimension = axis == "x" and dims.w - dims.ml - dims.mr or dims.h - dims.mt - dims.mb
local osd_dimension = axis == "x" and dims.w or dims.h
-- 1 video-align shifts the OSD by (dimension - osd_dimension) / 2 pixels,
-- so the equation to find how much video-align to add to offset the OSD by
-- osd_dimension is:
-- x/1 = osd_dimension / ((dimension - osd_dimension) / 2)
if dimension ~= osd_dimension then
mp.commandv("add", "video-align-" .. axis,
amount * 2 * osd_dimension / (dimension - osd_dimension))
end
end)

mp.add_key_binding(nil, "drag-to-pan", function (t)
if t.event == "up" then
mp.remove_key_binding("drag-to-pan-mouse-move")
return
end

local dims = mp.get_property_native("osd-dimensions")
local old_mouse_pos = mp.get_property_native("mouse-pos")
local old_align_x = mp.get_property_native("video-align-x")
local old_align_y = mp.get_property_native("video-align-y")

mp.add_forced_key_binding("MOUSE_MOVE", "drag-to-pan-mouse-move", function()
local mouse_pos = mp.get_property_native("mouse-pos")
-- 1 video-align shifts the OSD by (dimension - osd_dimension) / 2 pixels,
-- so the equation to find how much video-align to add to offset the OSD
-- by the difference in mouse position is:
-- x/1 = (mouse_pos - old_mouse_pos) / ((dimension - osd_dimension) / 2)
local align = old_align_x + 2 * (mouse_pos.x - old_mouse_pos.x)
/ (dims.ml + dims.mr)
mp.set_property("video-align-x", math.min(1, math.max(align, -1)))
align = old_align_y + 2 * (mouse_pos.y - old_mouse_pos.y)
/ (dims.mt + dims.mb)
mp.set_property("video-align-y", math.min(1, math.max(align, -1)))
end)
end, { complex = true })

mp.add_key_binding(nil, "align-to-cursor", function (t)
if t.event == "up" then
mp.remove_key_binding("align-to-cursor-mouse-move")
return
end

local dims = mp.get_property_native("osd-dimensions")
mp.add_forced_key_binding("MOUSE_MOVE", "align-to-cursor-mouse-move", function()
local mouse_pos = mp.get_property_native("mouse-pos")
mp.set_property("video-align-x", (mouse_pos.x * 2 - dims.w) / dims.w)
mp.set_property("video-align-y", (mouse_pos.y * 2 - dims.h) / dims.h)
end)
end, { complex = true })

mp.register_script_message("cursor-centric-zoom", function (amount)
local command = (options.suppress_osd and "no-osd " or "") ..
"add video-zoom " .. amount .. ";"

local x, y
local touch_positions = mp.get_property_native("touch-pos")
if next(touch_positions) then
for _, position in pairs(touch_positions) do
x = x + position.x
y = y + position.y
end
x = x / #touch_positions
y = y / #touch_positions
else
local mouse_pos = mp.get_property_native("mouse-pos")
x = mouse_pos.x
y = mouse_pos.y
end

local dims = mp.get_property_native("osd-dimensions")
local width = (dims.w - dims.ml - dims.mr) * 2^amount
local height = (dims.h - dims.mt - dims.mb) * 2^amount

local old_cursor_ml = dims.ml - x
local cursor_ml = old_cursor_ml * 2^amount
local ml = cursor_ml + x
-- video/out/aspect.c:src_dst_split_scaling() defines ml as:
-- ml = (osd-width - width) * (video-align-x + 1) / 2 + pan-x * width
-- So video-align-x is:
local align = 2 * (ml - mp.get_property_native("video-pan-x") * width)
/ (dims.w - width) - 1
command = command .. "no-osd set video-align-x " ..
math.min(1, math.max(align, -1)) .. ";"

local mt = (dims.mt - y) * 2^amount + y
align = 2 * (mt - mp.get_property_native("video-pan-y") * height)
/ (dims.h - height) - 1
command = command .. "no-osd set video-align-y " ..
math.min(1, math.max(align, -1))

mp.command(command)
end)
1 change: 1 addition & 0 deletions player/scripting.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ void mp_load_builtin_scripts(struct MPContext *mpctx)
load_builtin_script(mpctx, 4, mpctx->opts->lua_load_auto_profiles,
"@auto_profiles.lua");
load_builtin_script(mpctx, 5, mpctx->opts->lua_load_select, "@select.lua");
load_builtin_script(mpctx, 6, mpctx->opts->lua_load_positioning, "@positioning.lua");
}

bool mp_load_scripts(struct MPContext *mpctx)
Expand Down

0 comments on commit f0b043f

Please sign in to comment.