Skip to content

Commit

Permalink
split menu load logic to lua
Browse files Browse the repository at this point in the history
  • Loading branch information
tsl0922 committed Feb 4, 2024
1 parent 513e56e commit e0a16cb
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 457 deletions.
2 changes: 0 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ set(CMAKE_SHARED_LIBRARY_PREFIX "")
add_library(menu SHARED
src/mpv/misc/bstr.c
src/mpv/misc/dispatch.c
src/mpv/misc/node.c
src/mpv/ta/ta.c
src/mpv/ta/ta_talloc.c
src/mpv/ta/ta_utils.c
Expand All @@ -31,7 +30,6 @@ add_library(menu SHARED
set_property(TARGET menu PROPERTY POSITION_INDEPENDENT_CODE ON)

target_include_directories(menu PRIVATE src/mpv ${MPV_INCLUDE_DIRS})
target_link_libraries(menu PRIVATE shlwapi)
target_compile_definitions(menu PRIVATE MPV_CPLUGIN_DYNAMIC_SYM)

install(TARGETS menu RUNTIME DESTINATION .)
Expand Down
125 changes: 102 additions & 23 deletions src/lua/dyn_menu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ local msg = require('mp.msg')

-- user options
local o = {
uosc_syntax = false, -- toggle uosc menu syntax support
max_title_length = 80, -- limit the title length, set to 0 to disable.
max_playlist_items = 20, -- limit the playlist items in submenu, set to 0 to disable.
}
opts.read_options(o)

local menu_prop = 'user-data/menu/items' -- menu data property
local menu_items = mp.get_property_native(menu_prop, {}) -- raw menu data
local menu_items_dirty = false -- menu data dirty flag
local dyn_menus = {} -- dynamic menu list
local keyword_to_menu = {} -- keyword -> menu
local has_uosc = false -- uosc installed flag
local menu_prop = 'user-data/menu/items' -- menu data property
local menu_items = {} -- raw menu data
local menu_items_dirty = false -- menu data dirty flag
local dyn_menus = {} -- dynamic menu list
local keyword_to_menu = {} -- keyword -> menu
local has_uosc = false -- uosc installed flag

-- lua expression compiler (copied from mpv auto_profiles.lua)
------------------------------------------------------------------------
Expand Down Expand Up @@ -501,15 +502,6 @@ local function load_dyn_menus()
mp.commandv('script-message', 'menu-ready')
end

-- menu data update callback
local function menu_data_cb(name, items)
if not items or #items == 0 then return end
mp.unobserve_property(menu_data_cb)

menu_items = items
load_dyn_menus()
end

-- script message: get <keyword> <src>
mp.register_script_message('get', function(keyword, src)
if not src or src == '' then
Expand Down Expand Up @@ -570,13 +562,100 @@ mp.register_idle(function()
end
end)

-- parse menu data when menu items ready
--
-- NOTE: to simplify the code, we only procss the first valid update
-- event and ignore the rest, this make it conflict with other
-- scripts that also update the menu data property.
if #menu_items > 0 then
-- read input.conf content
local function get_input_conf()
local prop = mp.get_property_native('input-conf')
if prop:sub(1, 9) == 'memory://' then return prop:sub(10) end

prop = prop == '' and '~~/input.conf' or prop
local conf_path = mp.command_native({ 'expand-path', prop })

local f, err = io.open(conf_path, 'rb')
if not f then
msg.error('failed to open file: ' .. conf_path)
return nil
end

local conf = f:read('*all')
f:close()
return conf
end

-- parse input.conf, return menu items
local function parse_input_conf(conf)
local items = {}
local by_id = {}

local function extract_title(cmd)
if not cmd or cmd == '' then return '' end
local title = cmd:match('#menu:%s*(.*)%s*')
if not title and o.uosc_syntax then title = cmd:match('#!%s*(.*)%s*') end
return title or ''
end

local function split_title(title, ch)
local list = {}
if not title or title == '' then return list end
title = title:match('(.-)#%s*.-%s*$') or title -- strip trailing comment

local pattern = '(.-)%s*>%s*'
local last_ends = 1
local starts, ends, match = title:find(pattern)
while starts do
list[#list + 1] = match
last_ends = ends + 1
starts, ends, match = title:find(pattern, last_ends)
end
if last_ends < (#title + 1) then list[#list + 1] = title:sub(last_ends) end

return list
end

for line in conf:gmatch('[^\r\n]+') do
if line:sub(1, 1) ~= '#' or o.uosc_syntax then
local key, cmd = line:match('%s*([%S]+)%s+(.-)%s*$')
local title = extract_title(cmd)
local list = split_title(title)

local submenu_id = ''
local target_menu = items

for id, name in ipairs(list) do
if id < #list then
submenu_id = submenu_id .. name
if not by_id[submenu_id] then
local submenu = {}
by_id[submenu_id] = submenu
target_menu[#target_menu + 1] = {
title = name,
type = 'submenu',
submenu = submenu,
}
end
target_menu = by_id[submenu_id]
else
if name == '-' or (o.uosc_syntax and name:sub(1, 3) == '---') then
target_menu[#target_menu + 1] = {
type = 'separator',
}
else
target_menu[#target_menu + 1] = {
title = (key ~= '' and key ~= '_' and key ~= '#') and (name .. "\t" .. key) or name,
cmd = cmd,
}
end
end
end
end
end

return items
end

-- NOTE: this script is conflict with other scripts that also update the menu data property
local conf = get_input_conf()
if conf then
menu_items = parse_input_conf(conf)
menu_items_dirty = true
load_dyn_menus()
else
mp.observe_property(menu_prop, 'native', menu_data_cb)
end
120 changes: 5 additions & 115 deletions src/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-only

#include "misc/bstr.h"
#include "misc/node.h"
#include "menu.h"

#define MENU_PREFIX "#menu:"
#define MENU_PREFIX_UOSC "#!"

// escape & to && for menu title
static wchar_t *escape_title(void *talloc_ctx, char *title) {
void *tmp = talloc_new(NULL);
Expand Down Expand Up @@ -145,120 +141,14 @@ void build_menu(void *talloc_ctx, HMENU hmenu, mpv_node *node) {
}
}

static bool is_separator(bstr text, bool uosc) {
return bstr_equals0(text, "-") || (uosc && bstr_startswith0(text, "---"));
}

// return submenu node if exists, otherwise create a new one
static mpv_node *add_submenu(mpv_node *list, char *title) {
for (int i = 0; i < list->u.list->num; i++) {
mpv_node *item = &list->u.list->values[i];

mpv_node *type_node = node_map_get(item, "type");
if (!type_node || strcmp(type_node->u.string, "submenu") != 0) continue;

mpv_node *title_node = node_map_get(item, "title");
if (title_node && strcmp(title_node->u.string, title) == 0) {
mpv_node *submenu = node_map_get(item, "submenu");
if (!submenu)
submenu = node_map_add(item, "submenu", MPV_FORMAT_NODE_ARRAY);
return submenu;
}
}

mpv_node *node = node_array_add(list, MPV_FORMAT_NODE_MAP);
node_map_add_string(node, "title", title);
node_map_add_string(node, "type", "submenu");
return node_map_add(node, "submenu", MPV_FORMAT_NODE_ARRAY);
}

// parse menu line and add to menu node
static void parse_menu(mpv_node *list, bstr key, bstr cmd, bstr text,
bool uosc) {
bstr name, rest, comment;

name = bstr_split(text, ">", &rest);
name = bstr_split(name, "#", &comment);
name = bstr_strip(name);
if (!name.len) return;

void *tmp = talloc_new(NULL);

if (!rest.len) {
if (is_separator(name, uosc)) {
mpv_node *node = node_array_add(list, MPV_FORMAT_NODE_MAP);
node_map_add_string(node, "type", "separator");
} else {
bstr title = bstrdup(tmp, name);
if (key.len > 0 && !bstr_equals0(key, "_")) {
bstr_xappend(tmp, &title, bstr0("\t"));
bstr_xappend(tmp, &title, key);
}
mpv_node *node = node_array_add(list, MPV_FORMAT_NODE_MAP);
node_map_add_string(node, "title", bstrdup0(tmp, title));
node_map_add_string(node, "cmd", bstrdup0(tmp, cmd));
}
} else {
mpv_node *sub = add_submenu(list, bstrdup0(tmp, name));
if (!comment.len) parse_menu(sub, key, cmd, rest, uosc);
}

talloc_free(tmp);
}

static bool split_menu(bstr line, bstr *left, bstr *right, bool uosc) {
if (!line.len) return false;
if (!bstr_split_tok(line, MENU_PREFIX, left, right)) {
if (!uosc || !bstr_split_tok(line, MENU_PREFIX_UOSC, left, right))
return false;
}
*left = bstr_strip(*left);
*right = bstr_strip(*right);
return right->len > 0;
}

// build menu node from input.conf
void load_menu(mpv_node *node, plugin_config *conf) {
void *tmp = talloc_new(NULL);
char *path = mp_get_prop_string(tmp, "input-conf");
if (path == NULL || strlen(path) == 0) path = "~~/input.conf";

bstr data = bstr0(mp_read_file(tmp, path));
node_init(node, MPV_FORMAT_NODE_ARRAY, NULL);

while (data.len > 0) {
bstr line = bstr_strip_linebreaks(bstr_getline(data, &data));
line = bstr_lstrip(line);
if (!line.len) continue;

bstr key, cmd, left, right;
if (bstr_eatstart0(&line, "#")) {
if (!conf->uosc) continue;
key = bstr0(NULL);
cmd = bstr_strip(line);
} else {
key = bstr_split(line, WHITESPACE, &cmd);
cmd = bstr_strip(cmd);
}
if (split_menu(cmd, &left, &right, conf->uosc))
parse_menu(node, key, cmd, right, conf->uosc);
}

talloc_free(tmp);
}

// update HMENU if menu node changed
void update_menu(plugin_ctx *ctx, mpv_node *node) {
if (equal_mpv_node(ctx->node, node)) return;

copy_mpv_node(ctx->node, node);
if (!ctx->hmenu) return;

if (ctx->hmenu) {
while (GetMenuItemCount(ctx->hmenu) > 0)
RemoveMenu(ctx->hmenu, 0, MF_BYPOSITION);
talloc_free_children(ctx->hmenu_ctx);
build_menu(ctx->hmenu_ctx, ctx->hmenu, ctx->node);
}
while (GetMenuItemCount(ctx->hmenu) > 0)
RemoveMenu(ctx->hmenu, 0, MF_BYPOSITION);
talloc_free_children(ctx->hmenu_ctx);
build_menu(ctx->hmenu_ctx, ctx->hmenu, node);
}

// show menu at position if it is in window
Expand Down
1 change: 0 additions & 1 deletion src/menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

#define MENU_DATA_PROP "user-data/menu/items"

void load_menu(mpv_node *node, plugin_config *conf);
void build_menu(void *talloc_ctx, HMENU hmenu, mpv_node *node);
void update_menu(plugin_ctx *ctx, mpv_node *node);
void show_menu(plugin_ctx *ctx, POINT *pt);
Expand Down
1 change: 1 addition & 0 deletions src/mpv/misc/dispatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

typedef void (*mp_dispatch_fn)(void *data);
struct mp_dispatch_queue;
typedef struct mp_dispatch_queue mp_dispatch_queue;

struct mp_dispatch_queue *mp_dispatch_create(void *talloc_parent);
void mp_dispatch_set_wakeup_fn(struct mp_dispatch_queue *queue,
Expand Down
Loading

0 comments on commit e0a16cb

Please sign in to comment.