Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

positioning.lua: add this script #15314

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion DOCS/man/mpv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,12 @@ Left double click
Right click
Toggle pause on/off.

Middle click
Drag to pan through the whole video.

Middle double click
Reset pan.

Forward/Back button
Skip to next/previous entry in playlist.

Expand All @@ -354,7 +360,8 @@ Wheel left/right
Seek forward/backward 10 seconds.

Ctrl+Wheel up/down
Change video zoom.
Change video zoom keeping the part of the video hovered by the cursor under
it.

Context Menu
-------------
Expand Down Expand Up @@ -1482,6 +1489,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
51 changes: 51 additions & 0 deletions DOCS/man/positioning.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
POSITIONING
===========

This script provides script bindings to pan videos and images.

Script bindings
----------------------------

``pan-x <amount>``
Adjust ``--video-align-x`` relatively to the OSD width, rather than
relatively to the video width like the option. This is useful to pan large
images consistently.

``amount`` is a number such that an amount of 1 scrolls as much as the OSD
width.

``pan-y <amount>``
Adjust ``--video-align-y`` relatively to the OSD height, rather than
relatively to the video height like the option.

``amount`` is a number such that an amount of 1 scrolls as much as the OSD
height.

``drag-to-pan``
Pan the video while holding a mouse button, keeping the clicked part of the
video under the cursor.

``align-to-cursor``
Pan through the whole video while holding a mouse button, or after clicking
once if ``toggle_align_to_cursor`` is ``yes``.

``cursor-centric-zoom <amount>``
Increase ``--video-zoom`` by ``amount`` while keeping the part of the video
hovered by the cursor under it, or the average position of touch points if
known.

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

``toggle_align_to_cursor``
Default: no

Whether ``align-to-cursor`` requires holding down a mouse button to pan. If
``no``, dragging pans. If ``yes``, clicking the first time makes pan follow
the cursor, and clicking a second time disables this.

``suppress_osd``
Default: no

Whether to not print the new value of ``--video-zoom`` when using
``cursor-centric-zoom``.
8 changes: 5 additions & 3 deletions etc/input.conf
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#MBTN_LEFT ignore # don't do anything
#MBTN_LEFT_DBL cycle fullscreen # toggle fullscreen
#MBTN_RIGHT cycle pause # toggle pause/playback mode
#MBTN_MID script-binding positioning/align-to-cursor # pan the whole video
MBTN_MID_DBL no-osd set video-align-x 0; no-osd set video-align-y 0; no-osd set video-pan-x 0; no-osd set video-pan-y 0 # reset pan
#MBTN_BACK playlist-prev # skip to the previous file
#MBTN_FORWARD playlist-next # skip to the next file

Expand Down Expand Up @@ -65,9 +67,9 @@
#ZOOMIN add video-zoom 0.1 # zoom in
#Alt+- add video-zoom -0.1 # zoom out
#ZOOMOUT add video-zoom -0.1 # zoom out
#Ctrl+WHEEL_UP add video-zoom 0.1 # zoom in
#Ctrl+WHEEL_DOWN add video-zoom -0.1 # zoom out
#Alt+BS set video-zoom 0; set panscan 0; set video-pan-x 0; set video-pan-y 0 # reset zoom and pan settings
#Ctrl+WHEEL_UP script-binding positioning/cursor-centric-zoom 0.1 # zoom in towards the cursor
#Ctrl+WHEEL_DOWN script-binding positioning/cursor-centric-zoom -0.1 # zoom out towards the cursor
Comment on lines +70 to +71
Copy link
Contributor

@kasper93 kasper93 Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it need to be changed? Could we use Ctrl+Alt modifier for new behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason not to have cursor-centric-zoom on the wheel? It is more convenient, also according to nanahi ctrl+wheel "makes "pinch to zoom" with touchpads and touchscreens work out of box on Windows, since by default applications receive these key inputs for pinch gesture.", so it needs to be bind to ctrl+wheel to work with pinch gestures by default.

I have no idea what numpad bindings have to do with cursor-centric-zoom, the numpad is not a cursor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason not to have cursor-centric-zoom on the wheel? It is more convenient, also according to nanahi ctrl+wheel "makes "pinch to zoom" with touchpads and touchscreens work out of box on Windows, since by default applications receive these key inputs for pinch gesture.", so it needs to be bind to ctrl+wheel to work with pinch gestures by default.

Ok, if you think it is more convenient this way. I think it is still valuable to control zoom without messing up video positioning completely on each step. But I guess by default it can be cursor centric.

Note that same issues applies, if lua is disabled in mpv build, it probably should fallback to normal video-zoom.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can still use the Alt key bindings for that, we didn't even have Ctrl+wheel until recently.

I removed the Alt+Arrow bindings so you do want to implement a fallback system just for this? I guess we can define alternative bindings with some keyword that input.c can recognize?

#Alt+BS set video-zoom 0; no-osd set panscan 0; no-osd set video-pan-x 0; no-osd set video-pan-y 0; no-osd set video-align-x 0; no-osd set video-align-y 0 # reset zoom and pan settings
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could update numpad bindings the same way.

#PGUP add chapter 1 # seek to the next chapter
#PGDWN add chapter -1 # seek to the previous chapter
#Shift+PGUP seek 600 # seek 10 minutes forward
Expand Down
2 changes: 2 additions & 0 deletions options/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,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},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used int defualt input.conf, I don't know if it is good to allow unloading this script. But maybe needed in some cases.

EDIT: We should probably merge all those load- into one string list option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--load options let you use a fork of that script in your scripts directory which was one of the arguments for making this a script. They are also convenient for development without recompiling.

#endif

// ------------------------- stream options --------------------
Expand Down Expand Up @@ -966,6 +967,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 @@ -170,6 +170,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 @@ -443,7 +443,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
157 changes: 157 additions & 0 deletions player/lua/positioning.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
--[[
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 = {
toggle_align_to_cursor = false,
suppress_osd = false,
}

require "mp.options".read_options(options, nil, function () end)

mp.add_key_binding(nil, "pan-x", function (t)
if t.event == "up" then
return
end

local dims = mp.get_property_native("osd-dimensions")
local width = dims.w - dims.ml - dims.mr
-- 1 video-align shifts the OSD by (width - osd-width) / 2 pixels, so the
-- equation to find how much video-align to add to offset the OSD by
-- osd-width is:
-- x/1 = osd-width / ((width - osd-width) / 2)
if width ~= dims.w then
mp.commandv("add", "video-align-x",
t.arg * t.scale * 2 * dims.w / (width - dims.w))
end
end, { complex = true, scalable = true })

mp.add_key_binding(nil, "pan-y", function (t)
if t.event == "up" then
return
end

local dims = mp.get_property_native("osd-dimensions")
local height = dims.h - dims.mt - dims.mb
if height ~= dims.h then
mp.commandv("add", "video-align-y",
t.arg * t.scale * 2 * dims.h / (height - dims.h))
end
end, { complex = true, scalable = true })

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 })


local align_to_cursor_bound = false

local function align_to_cursor(_, mouse_pos)
local dims = mp.get_property_native("osd-dimensions")
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

mp.add_key_binding(nil, "align-to-cursor", function (t)
if options.toggle_align_to_cursor == false then
if t.event == "down" then
mp.observe_property("mouse-pos", "native", align_to_cursor)
else
mp.unobserve_property(align_to_cursor)
end

return
end

if t.event ~= "up" then
return
end

if align_to_cursor_bound then
mp.unobserve_property(align_to_cursor)
else
mp.observe_property("mouse-pos", "native", align_to_cursor)
end
align_to_cursor_bound = not align_to_cursor_bound
end, { complex = true })


mp.add_key_binding(nil, "cursor-centric-zoom", function (t)
local amount = t.arg * t.scale

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, { complex = true, scalable = true })
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