diff options
| -rw-r--r-- | main.lua | 239 |
1 files changed, 124 insertions, 115 deletions
@@ -16,7 +16,9 @@ local script_name = mp.get_script_name() local script_dir = mp.get_script_directory() local state = _state.new() +local binding_state = {mappings = {}, active = {}} local click_state = {} +local osc_visibility local downloader = _downloader.new({limit = 5}) local xc = _xc.new({ @@ -58,9 +60,6 @@ local osd = _osd.new({ end, }) -local key_bindings = {} -local osc_visibility - local function update_osd() osd:redraw(state) end @@ -69,6 +68,10 @@ local function osd_menu_lines() return osd:menu_lines(state) end +local function set_key_mapping(m) + binding_state.active = binding_state.mappings[m] +end + local function load_data() local arr = { {id = 'live', name = 'Live TV', type = 'live'}, @@ -812,17 +815,17 @@ local function search_menu_options(options) return ret end -local function search_input_char(event) - if event.event ~= 'down' and event.event ~= 'repeat' then +local function search_input_char(ev) + if ev.event ~= 'down' and ev.event ~= 'repeat' then return end local menu = state:menu() menu:set_search_text( menu.search_text:sub(1, menu.search_cursor - 1) .. - event.key_text .. + ev.key_text .. menu.search_text:sub(menu.search_cursor)) - menu:set_search_cursor(menu.search_cursor + #event.key_text) + menu:set_search_cursor(menu.search_cursor + #ev.key_text) update_osd() end @@ -879,9 +882,6 @@ local function search_cursor_end() set_search_cursor(#state:menu().search_text + 1) end -local bind_search_keys -local bind_menu_keys - local function start_search() local menu = state:menu() local title = 'Searching: <text_with_cursor>' .. @@ -907,7 +907,7 @@ local function start_search() end update_osd() - bind_search_keys() + set_key_mapping('SEARCH') end local function end_search() @@ -916,7 +916,7 @@ local function end_search() menu.title = 'Search results: <text>' .. ' <colours.info>(<num_matches>/<num_total>)' update_osd() - bind_menu_keys() + set_key_mapping('MENU') end local function cancel_search() @@ -934,7 +934,7 @@ local function cancel_search() menu.search_active = false state.depth = state.depth - 1 update_osd() - bind_menu_keys() + set_key_mapping('MENU') end local function toggle_menu_sort() @@ -1080,115 +1080,125 @@ local function mouse_click_right(ev) open_option_info(menu.options[pos]) end -local function bind_key(key, func, opts) - -- unique name is needed for removal - local i = #key_bindings+1 - local name = 'key' .. i - key_bindings[i] = name - mp.add_forced_key_binding(key, name, func, opts) -end - -local function unbind_keys() - for _, key in ipairs(key_bindings) do - mp.remove_key_binding(key) - end - key_bindings = {} -end - -function bind_search_keys() - unbind_keys() - - local c = {complex = true} - local r = {repeatable = true} - - bind_key('ANY_UNICODE', search_input_char, c) - bind_key('BS', search_input_bs, r) - bind_key('DEL', search_input_del, r) - - bind_key('ENTER', end_search) - bind_key('ESC', cancel_search) - bind_key('Ctrl+c', cancel_search) - - bind_key('LEFT', search_cursor_left, r) - bind_key('RIGHT', search_cursor_right, r) - bind_key('Ctrl+a', search_cursor_start) - bind_key('Ctrl+e', search_cursor_end) -end - -function bind_menu_keys() - unbind_keys() - - local c = {complex = true} - local r = {repeatable = true} - - bind_key('BS', prev_menu) - bind_key('/', start_search) - bind_key('Ctrl+s', toggle_menu_sort) - - bind_key('ENTER', select_option) - bind_key('Ctrl+f', favourite_option) - bind_key('g', goto_option) - bind_key('i', open_option_info) - bind_key('?', open_option_info) - bind_key('Ctrl+p', goto_playing) - - bind_key('MBTN_LEFT', mouse_click_left, c) - bind_key('MBTN_RIGHT', mouse_click_right, c) - bind_key('MBTN_LEFT_DBL') - bind_key('MBTN_RIGHT_DBL') - - bind_key('k', cursor_up, r) - bind_key('j', cursor_down, r) - bind_key('K', cursor_page_up, r) - bind_key('J', cursor_page_down, r) - bind_key('UP', cursor_up, r) - bind_key('DOWN', cursor_down, r) - bind_key('Shift+UP', cursor_page_up, r) - bind_key('Shift+DOWN', cursor_page_down, r) - bind_key('PGUP', cursor_page_up, r) - bind_key('PGDWN', cursor_page_down, r) - bind_key('HOME', cursor_start) - bind_key('END', cursor_end) - bind_key('WHEEL_UP', cursor_up, r) - bind_key('WHEEL_DOWN', cursor_down, r) - bind_key('Shift+WHEEL_UP', cursor_page_up, r) - bind_key('Shift+WHEEL_DOWN', cursor_page_down, r) - - bind_key('Alt+k', move_option_up, r) - bind_key('Alt+j', move_option_down, r) - bind_key('Alt+K', move_option_page_up, r) - bind_key('Alt+J', move_option_page_down, r) - bind_key('Alt+UP', move_option_up, r) - bind_key('Alt+DOWN', move_option_down, r) - bind_key('Alt+Shift+UP', move_option_page_up, r) - bind_key('Alt+Shift+DOWN', move_option_page_down, r) - bind_key('Alt+PGUP', move_option_page_up, r) - bind_key('Alt+PGDWN', move_option_page_down, r) - bind_key('Alt+HOME', move_option_start) - bind_key('Alt+END', move_option_end) - bind_key('Alt+WHEEL_UP', move_option_up, r) - bind_key('Alt+WHEEL_DOWN', move_option_down, r) - bind_key('Alt+Shift+WHEEL_UP', move_option_page_up, r) - bind_key('Alt+Shift+WHEEL_DOWN', move_option_page_down, r) +binding_state.mappings.MENU = { + ['BS'] = {prev_menu}, + ['/'] = {start_search}, + ['Ctrl+s'] = {toggle_menu_sort}, + + ['ENTER'] = {select_option}, + ['Ctrl+f'] = {favourite_option}, + ['g'] = {goto_option}, + ['i'] = {open_option_info}, + ['?'] = {open_option_info}, + ['Ctrl+p'] = {goto_playing}, + + ['MBTN_LEFT'] = {mouse_click_left, 'complex'}, + ['MBTN_RIGHT'] = {mouse_click_right, 'complex'}, + + ['k'] = {cursor_up, 'repeat'}, + ['j'] = {cursor_down, 'repeat'}, + ['K'] = {cursor_page_up, 'repeat'}, + ['J'] = {cursor_page_down, 'repeat'}, + ['UP'] = {cursor_up, 'repeat'}, + ['DOWN'] = {cursor_down, 'repeat'}, + ['Shift+UP'] = {cursor_page_up, 'repeat'}, + ['Shift+DOWN'] = {cursor_page_down, 'repeat'}, + ['PGUP'] = {cursor_page_up, 'repeat'}, + ['PGDWN'] = {cursor_page_down, 'repeat'}, + ['HOME'] = {cursor_start}, + ['END'] = {cursor_end}, + ['WHEEL_UP'] = {cursor_up, 'repeat'}, + ['WHEEL_DOWN'] = {cursor_down, 'repeat'}, + ['Shift+WHEEL_UP'] = {cursor_page_up, 'repeat'}, + ['Shift+WHEEL_DOWN'] = {cursor_page_down, 'repeat'}, + + ['Alt+k'] = {move_option_up, 'repeat'}, + ['Alt+j'] = {move_option_down, 'repeat'}, + ['Alt+K'] = {move_option_page_up, 'repeat'}, + ['Alt+J'] = {move_option_page_down, 'repeat'}, + ['Alt+UP'] = {move_option_up, 'repeat'}, + ['Alt+DOWN'] = {move_option_down, 'repeat'}, + ['Shift+Alt+UP'] = {move_option_page_up, 'repeat'}, + ['Shift+Alt+DOWN'] = {move_option_page_down, 'repeat'}, + ['Alt+PGUP'] = {move_option_page_up, 'repeat'}, + ['Alt+PGDWN'] = {move_option_page_down, 'repeat'}, + ['Alt+HOME'] = {move_option_start}, + ['Alt+END'] = {move_option_end}, + ['Alt+WHEEL_UP'] = {move_option_up, 'repeat'}, + ['Alt+WHEEL_DOWN'] = {move_option_down, 'repeat'}, + ['Shift+Alt+WHEEL_UP'] = {move_option_page_up, 'repeat'}, + ['Shift+Alt+WHEEL_DOWN'] = {move_option_page_down, 'repeat'}, +} + +binding_state.mappings.SEARCH = { + ['ANY_UNICODE'] = {search_input_char, 'complex'}, + ['BS'] = {search_input_bs, 'repeat'}, + ['DEL'] = {search_input_del, 'repeat'}, + + ['ENTER'] = {end_search}, + ['ESC'] = {cancel_search}, + ['Ctrl+c'] = {cancel_search}, + + ['LEFT'] = {search_cursor_left, 'repeat'}, + ['RIGHT'] = {search_cursor_right, 'repeat'}, + ['Ctrl+a'] = {search_cursor_start}, + ['Ctrl+e'] = {search_cursor_end}, +} + +-- mpv does not process key-binding changes requested by script functions until +-- those functions return. in the meantime, while the function is running, mpv +-- discards any key presses for keys that were not already bound prior to the +-- start of function execution. similarly, any pending key presses (for keys +-- that were bound) are discarded if the binding changes during function +-- execution. +-- +-- in other words, we get an inconsistent result where pressing a key while a +-- function is running triggers the bound function upon completion if that +-- binding remains the same, but ignores such key presses when our function +-- changes the corresponding binding or adds a new one. we can avoid this by +-- leaving our keys bound to a common function and building our own logic to +-- route keys per the current state. +local function handle_key(ev) + t = binding_state.active[ev.key_name] + if not t and ev.key_text then + t = binding_state.active['ANY_UNICODE'] + end + + f = t and t[1] + if not f then + return + end + + flag = t[2] + if flag == 'complex' then + f(ev) + elseif ev.event == 'down' or + (ev.event == 'repeat' and flag == 'repeat') then + f() + end end -- uses enable-section and disable-section to disable builtin key bindings -- while the OSD is visible. these commands are technically deprecated for -- non-internal use, but they still work, and there doesn't appear to be -- another way apart from setting an override for each individual key. --- --- might eventually change this to a selective override, since some of these --- builtin keys could still be useful while the menus are open. local function set_key_bindings() if osd:is_hidden() then - unbind_keys() - mp.command_native({'enable-section', 'default'}) - elseif state:menu().search_active then - bind_search_keys() - mp.command_native({'disable-section', 'default'}) - else - bind_menu_keys() + if binding_state.bound then + mp.remove_key_binding('mouse_move') + mp.remove_key_binding('unmapped') + mp.command_native({'enable-section', 'default'}) + binding_state.bound = false + end + return + end + + if not binding_state.bound then mp.command_native({'disable-section', 'default'}) + mp.add_forced_key_binding('MOUSE_MOVE', 'mouse_move') -- noisy + mp.add_forced_key_binding( + 'UNMAPPED', 'unmapped', handle_key, {complex = true}) + binding_state.bound = true end end @@ -1234,7 +1244,6 @@ push_group_menu(catalogue:get('root')) osc_visibility = mp.get_property_native('user-data/osc/visibility', 'auto') set_osc_visibility() --- keys added via bind_key() are unbound when the OSD is closed, but we want --- toggle-menu to work regardless of setting. mp.add_forced_key_binding('TAB', 'toggle-menu', toggle_menu) +set_key_mapping('MENU') set_key_bindings() |
