History

positioning.lua: add this script

This script provides script bindings to pan videos and images, making mpv a better image viewer out of the box. Fixes #3038, fixes #15020.

Guido Cella committed 1 year ago

Commit aa5d37b1 parent 64531af10 files · +230 −2Browse files

Unified · Split

10 files
  1. DOCS/man/mpv.rst +2 −0
  2. DOCS/man/options.rst +4 −0
  3. DOCS/man/positioning.rst +60 −0
  4. options/options.c +2 −0
  5. options/options.h +1 −0
  6. player/core.h +1 −1
  7. player/lua.c +3 −0
  8. player/lua/meson.build +1 −1
  9. player/lua/positioning.lua +155 −0
  10. player/scripting.c +1 −0
DOCS/man/mpv.rst+2−0
@@ -1532,6 +1532,8 @@ works like in older mpv releases: · 1531 unmodified lines
15321532
15331533 .. include:: select.rst
15341534
1535+.. include:: positioning.rst
1536+
15351537 .. include:: lua.rst
15361538
15371539 .. include:: javascript.rst
DOCS/man/options.rst+4−0
@@ -1067,6 +1067,10 @@ Program Behavior · 1066 unmodified lines
10671067 Enable the builtin script that lets you select from lists of items (default:
10681068 yes). By default, its keybindings start with the ``g`` key.
10691069
1070+``--load-positioning=<yes|no>``
1071+ Enable the builtin script that provides various keybindings to pan videos
1072+ and images (default: yes).
1073+
10701074 ``--player-operation-mode=<cplayer|pseudo-gui>``
10711075 For enabling "pseudo GUI mode", which means that the defaults for some
10721076 options are changed. This option should not normally be used directly, but
DOCS/man/positioning.rst+60−0
@@ -0,0 +1,60 @@
1+POSITIONING
2+===========
3+
4+This script provides script bindings to pan videos and images. It can be
5+disabled using the ``--load-positioning=no`` option.
6+
7+Script bindings
8+---------------
9+
10+``pan-x <amount>``
11+ Adjust ``--video-align-x`` relatively to the OSD width, rather than
12+ relatively to the video width like the option. This is useful to pan large
13+ images consistently.
14+
15+ ``amount`` is a number such that an amount of 1 scrolls as much as the OSD
16+ width.
17+
18+``pan-y <amount>``
19+ Adjust ``--video-align-y`` relatively to the OSD height, rather than
20+ relatively to the video height like the option.
21+
22+ ``amount`` is a number such that an amount of 1 scrolls as much as the OSD
23+ height.
24+
25+``drag-to-pan``
26+ Pan the video while holding a mouse button, keeping the clicked part of the
27+ video under the cursor.
28+
29+``align-to-cursor``
30+ Pan through the whole video while holding a mouse button, or after clicking
31+ once if ``toggle_align_to_cursor`` is ``yes``.
32+
33+``cursor-centric-zoom <amount>``
34+ Increase ``--video-zoom`` by ``amount`` while keeping the part of the video
35+ hovered by the cursor under it, or the average position of touch points if
36+ known.
37+
38+Configuration
39+-------------
40+
41+This script can be customized through a config file
42+``script-opts/positioning.conf`` placed in mpv's user directory and through the
43+``--script-opts`` command-line option. The configuration syntax is described in
44+`mp.options functions`_.
45+
46+Configurable Options
47+~~~~~~~~~~~~~~~~~~~~
48+
49+``toggle_align_to_cursor``
50+ Default: no
51+
52+ Whether ``align-to-cursor`` requires holding down a mouse button to pan. If
53+ ``no``, dragging pans. If ``yes``, clicking the first time makes pan follow
54+ the cursor, and clicking a second time disables this.
55+
56+``suppress_osd``
57+ Default: no
58+
59+ Whether to not print the new value of ``--video-zoom`` when using
60+ ``cursor-centric-zoom``.
options/options.c+2−0
@@ -554,6 +554,7 @@ static const m_option_t mp_opts[] = { · 553 unmodified lines
554554 OPT_CHOICE(lua_load_auto_profiles, {"no", 0}, {"yes", 1}, {"auto", -1}),
555555 .flags = UPDATE_BUILTIN_SCRIPTS},
556556 {"load-select", OPT_BOOL(lua_load_select), .flags = UPDATE_BUILTIN_SCRIPTS},
557+ {"load-positioning", OPT_BOOL(lua_load_positioning), .flags = UPDATE_BUILTIN_SCRIPTS},
557558 #endif
558559
559560 // ------------------------- stream options --------------------
@@ -986,6 +987,7 @@ static const struct MPOpts mp_default_opts = { · 426 unmodified lines
986987 .lua_load_console = true,
987988 .lua_load_auto_profiles = -1,
988989 .lua_load_select = true,
990+ .lua_load_positioning = true,
989991 #endif
990992 .auto_load_scripts = true,
991993 .loop_times = 1,
options/options.h+1−0
@@ -174,6 +174,7 @@ typedef struct MPOpts { · 173 unmodified lines
174174 bool lua_load_console;
175175 int lua_load_auto_profiles;
176176 bool lua_load_select;
177+ bool lua_load_positioning;
177178
178179 bool auto_load_scripts;
179180
player/core.h+1−1
@@ -444,7 +444,7 @@ typedef struct MPContext { · 443 unmodified lines
444444
445445 struct mp_ipc_ctx *ipc_ctx;
446446
447− int64_t builtin_script_ids[6];
447+ int64_t builtin_script_ids[7];
448448
449449 mp_mutex abort_lock;
450450
player/lua.c+3−0
@@ -84,6 +84,9 @@ static const char * const builtin_lua_scripts[][2] = { · 83 unmodified lines
8484 },
8585 {"@select.lua",
8686 # include "player/lua/select.lua.inc"
87+ },
88+ {"@positioning.lua",
89+# include "player/lua/positioning.lua.inc"
8790 },
8891 {0}
8992 };
player/lua/meson.build+1−1
@@ -1,6 +1,6 @@
11 lua_files = ['defaults.lua', 'assdraw.lua', 'options.lua', 'osc.lua',
22 'ytdl_hook.lua', 'stats.lua', 'console.lua', 'auto_profiles.lua',
3− 'input.lua', 'fzy.lua', 'select.lua']
3+ 'input.lua', 'fzy.lua', 'select.lua', 'positioning.lua']
44 foreach file: lua_files
55 lua_file = custom_target(file,
66 input: file,
player/lua/positioning.lua+155−0
@@ -0,0 +1,155 @@
1+--[[
2+This file is part of mpv.
3+
4+mpv is free software; you can redistribute it and/or
5+modify it under the terms of the GNU Lesser General Public
6+License as published by the Free Software Foundation; either
7+version 2.1 of the License, or (at your option) any later version.
8+
9+mpv is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU Lesser General Public License for more details.
13+
14+You should have received a copy of the GNU Lesser General Public
15+License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16+]]
17+
18+local options = {
19+ toggle_align_to_cursor = false,
20+ suppress_osd = false,
21+}
22+
23+require "mp.options".read_options(options, nil, function () end)
24+
25+local function clamp(value, min, max)
26+ return math.min(math.max(value, min), max)
27+end
28+
29+mp.add_key_binding(nil, "pan-x", function (t)
30+ if t.event == "up" then
31+ return
32+ end
33+
34+ local dims = mp.get_property_native("osd-dimensions")
35+ local width = dims.w - dims.ml - dims.mr
36+ -- 1 video-align shifts the OSD by (width - osd-width) / 2 pixels, so the
37+ -- equation to find how much video-align to add to offset the OSD by
38+ -- osd-width is:
39+ -- x/1 = osd-width / ((width - osd-width) / 2)
40+ local amount = t.arg * t.scale * 2 * dims.w / (width - dims.w)
41+ mp.commandv("add", "video-align-x", clamp(amount, -2, 2))
42+end, { complex = true, scalable = true })
43+
44+mp.add_key_binding(nil, "pan-y", function (t)
45+ if t.event == "up" then
46+ return
47+ end
48+
49+ local dims = mp.get_property_native("osd-dimensions")
50+ local height = dims.h - dims.mt - dims.mb
51+ local amount = t.arg * t.scale * 2 * dims.h / (height - dims.h)
52+ mp.commandv("add", "video-align-y", clamp(amount, -2, 2))
53+end, { complex = true, scalable = true })
54+
55+mp.add_key_binding(nil, "drag-to-pan", function (t)
56+ if t.event == "up" then
57+ mp.remove_key_binding("drag-to-pan-mouse-move")
58+ return
59+ end
60+
61+ local dims = mp.get_property_native("osd-dimensions")
62+ local old_mouse_pos = mp.get_property_native("mouse-pos")
63+ local old_align_x = mp.get_property_native("video-align-x")
64+ local old_align_y = mp.get_property_native("video-align-y")
65+
66+ mp.add_forced_key_binding("MOUSE_MOVE", "drag-to-pan-mouse-move", function ()
67+ local mouse_pos = mp.get_property_native("mouse-pos")
68+ -- 1 video-align shifts the OSD by (dimension - osd_dimension) / 2 pixels,
69+ -- so the equation to find how much video-align to add to offset the OSD
70+ -- by the difference in mouse position is:
71+ -- x/1 = (mouse_pos - old_mouse_pos) / ((dimension - osd_dimension) / 2)
72+ local align = old_align_x + 2 * (mouse_pos.x - old_mouse_pos.x)
73+ / (dims.ml + dims.mr)
74+ mp.set_property("video-align-x", clamp(align, -1, 1))
75+ align = old_align_y + 2 * (mouse_pos.y - old_mouse_pos.y)
76+ / (dims.mt + dims.mb)
77+ mp.set_property("video-align-y", clamp(align, -1, 1))
78+ end)
79+end, { complex = true })
80+
81+
82+local align_to_cursor_bound = false
83+
84+local function align_to_cursor(_, mouse_pos)
85+ local dims = mp.get_property_native("osd-dimensions")
86+ mp.set_property("video-align-x", (mouse_pos.x * 2 - dims.w) / dims.w)
87+ mp.set_property("video-align-y", (mouse_pos.y * 2 - dims.h) / dims.h)
88+end
89+
90+mp.add_key_binding(nil, "align-to-cursor", function (t)
91+ if options.toggle_align_to_cursor == false then
92+ if t.event == "down" then
93+ mp.observe_property("mouse-pos", "native", align_to_cursor)
94+ else
95+ mp.unobserve_property(align_to_cursor)
96+ end
97+
98+ return
99+ end
100+
101+ if t.event ~= "up" then
102+ return
103+ end
104+
105+ if align_to_cursor_bound then
106+ mp.unobserve_property(align_to_cursor)
107+ else
108+ mp.observe_property("mouse-pos", "native", align_to_cursor)
109+ end
110+ align_to_cursor_bound = not align_to_cursor_bound
111+end, { complex = true })
112+
113+
114+mp.add_key_binding(nil, "cursor-centric-zoom", function (t)
115+ local amount = t.arg * t.scale
116+
117+ local command = (options.suppress_osd and "no-osd " or "") ..
118+ "add video-zoom " .. amount .. ";"
119+
120+ local x, y
121+ local touch_positions = mp.get_property_native("touch-pos")
122+ if touch_positions[1] then
123+ for _, position in pairs(touch_positions) do
124+ x = x + position.x
125+ y = y + position.y
126+ end
127+ x = x / #touch_positions
128+ y = y / #touch_positions
129+ else
130+ local mouse_pos = mp.get_property_native("mouse-pos")
131+ x = mouse_pos.x
132+ y = mouse_pos.y
133+ end
134+
135+ local dims = mp.get_property_native("osd-dimensions")
136+ local width = (dims.w - dims.ml - dims.mr) * 2^amount
137+ local height = (dims.h - dims.mt - dims.mb) * 2^amount
138+
139+ local old_cursor_ml = dims.ml - x
140+ local cursor_ml = old_cursor_ml * 2^amount
141+ local ml = cursor_ml + x
142+ -- video/out/aspect.c:src_dst_split_scaling() defines ml as:
143+ -- ml = (osd-width - width) * (video-align-x + 1) / 2 + pan-x * width
144+ -- So video-align-x is:
145+ local align = 2 * (ml - mp.get_property_native("video-pan-x") * width)
146+ / (dims.w - width) - 1
147+ command = command .. "no-osd set video-align-x " .. clamp(align, -1, 1) .. ";"
148+
149+ local mt = (dims.mt - y) * 2^amount + y
150+ align = 2 * (mt - mp.get_property_native("video-pan-y") * height)
151+ / (dims.h - height) - 1
152+ command = command .. "no-osd set video-align-y " .. clamp(align, -1, 1)
153+
154+ mp.command(command)
155+end, { complex = true, scalable = true })
player/scripting.c+1−0
@@ -263,6 +263,7 @@ void mp_load_builtin_scripts(struct MPContext *mpctx) · 262 unmodified lines
263263 load_builtin_script(mpctx, 4, mpctx->opts->lua_load_auto_profiles,
264264 "@auto_profiles.lua");
265265 load_builtin_script(mpctx, 5, mpctx->opts->lua_load_select, "@select.lua");
266+ load_builtin_script(mpctx, 6, mpctx->opts->lua_load_positioning, "@positioning.lua");
266267 }
267268
268269 bool mp_load_scripts(struct MPContext *mpctx)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t