From f0b043fff8fa9a8e06dce276dbd1f481713e2f10 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 16 Nov 2024 14:18:09 +0100 Subject: [PATCH] positioning.lua: add this script This script provides script messages and bindings to pan videos and images, making mpv a better image viewer out of the box. --- DOCS/man/mpv.rst | 2 + DOCS/man/options.rst | 4 ++ DOCS/man/positioning.rst | 36 +++++++++++ options/options.c | 2 + options/options.h | 1 + player/core.h | 2 +- player/lua.c | 3 + player/lua/meson.build | 2 +- player/lua/positioning.lua | 119 +++++++++++++++++++++++++++++++++++++ player/scripting.c | 1 + 10 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 DOCS/man/positioning.rst create mode 100644 player/lua/positioning.lua diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst index 64de51d112c13..6f11cd11b5d1a 100644 --- a/DOCS/man/mpv.rst +++ b/DOCS/man/mpv.rst @@ -1461,6 +1461,8 @@ works like in older mpv releases: .. include:: console.rst +.. include:: positioning.rst + .. include:: lua.rst .. include:: javascript.rst diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index d32bcac9d4b88..c68a95b20edda 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -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=`` + Enable the builtin script that provides various keybindings to pan videos + and images (default: yes). + ``--player-operation-mode=`` For enabling "pseudo GUI mode", which means that the defaults for some options are changed. This option should not normally be used directly, but diff --git a/DOCS/man/positioning.rst b/DOCS/man/positioning.rst new file mode 100644 index 0000000000000..51619e2e4d187 --- /dev/null +++ b/DOCS/man/positioning.rst @@ -0,0 +1,36 @@ +POSITIONING +=========== + +This script provides key bindings to pan videos and images. + +Script messages and bindings +---------------------------- + +``pan `` + 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 `` + 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``. diff --git a/options/options.c b/options/options.c index b1d5eacfaf09d..8de8f69df9837 100644 --- a/options/options.c +++ b/options/options.c @@ -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 -------------------- @@ -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, diff --git a/options/options.h b/options/options.h index 347746f7c8696..bc12cab85c204 100644 --- a/options/options.h +++ b/options/options.h @@ -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; diff --git a/player/core.h b/player/core.h index dce3ce47342cc..bbc31bb826734 100644 --- a/player/core.h +++ b/player/core.h @@ -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; diff --git a/player/lua.c b/player/lua.c index 9ccd1f4d4432d..fdf53437af33e 100644 --- a/player/lua.c +++ b/player/lua.c @@ -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} }; diff --git a/player/lua/meson.build b/player/lua/meson.build index ac4c54b4dd4fd..a8b8704957608 100644 --- a/player/lua/meson.build +++ b/player/lua/meson.build @@ -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, diff --git a/player/lua/positioning.lua b/player/lua/positioning.lua new file mode 100644 index 0000000000000..4004f21ae062b --- /dev/null +++ b/player/lua/positioning.lua @@ -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 . +]] + +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) diff --git a/player/scripting.c b/player/scripting.c index a33b61805692e..2b5cf4576226f 100644 --- a/player/scripting.c +++ b/player/scripting.c @@ -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)