From 796eae2978a9ad2929acfc39df3104a9d5346302 Mon Sep 17 00:00:00 2001 From: Shuanglei Tao Date: Sun, 4 Feb 2024 18:36:57 +0800 Subject: [PATCH] move menu load logic to lua --- src/lua/dyn_menu.lua | 124 +++++++++++++++++++++++++++++++++++-------- src/menu.c | 105 ------------------------------------ src/menu.h | 1 - src/plugin.c | 7 --- src/plugin.h | 1 - 5 files changed, 101 insertions(+), 137 deletions(-) diff --git a/src/lua/dyn_menu.lua b/src/lua/dyn_menu.lua index 6ceb403..0bfda4d 100644 --- a/src/lua/dyn_menu.lua +++ b/src/lua/dyn_menu.lua @@ -12,12 +12,12 @@ local o = { } 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) ------------------------------------------------------------------------ @@ -501,15 +501,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 mp.register_script_message('get', function(keyword, src) if not src or src == '' then @@ -570,13 +561,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 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 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 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 diff --git a/src/menu.c b/src/menu.c index ba810e0..b4344d6 100644 --- a/src/menu.c +++ b/src/menu.c @@ -5,9 +5,6 @@ #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); @@ -145,108 +142,6 @@ 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; diff --git a/src/menu.h b/src/menu.h index 80182d5..2fc7802 100644 --- a/src/menu.h +++ b/src/menu.h @@ -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); diff --git a/src/plugin.c b/src/plugin.c index 81d0c02..0bf3d13 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -116,7 +116,6 @@ static void read_conf(plugin_config *conf, const char *name) { value = bstr_strip(value); if (name.len == 0 || value.len == 0) continue; - if (bstr_equals0(name, "load")) conf->load = bstr_equals0(value, "yes"); if (bstr_equals0(name, "uosc")) conf->uosc = bstr_equals0(value, "yes"); } @@ -227,7 +226,6 @@ static void create_plugin_ctx(mpv_handle *mpv) { ctx->mpv = mpv; ctx->conf = talloc_zero(ctx, plugin_config); - ctx->conf->load = true; read_conf(ctx->conf, mpv_client_name(mpv)); ctx->dispatch = mp_dispatch_create(ctx); @@ -248,11 +246,6 @@ MPV_EXPORT int mpv_open_cplugin(mpv_handle *handle) { create_plugin_ctx(handle); - if (ctx->conf->load) { - load_menu(ctx->node, ctx->conf); - mpv_set_property(handle, MENU_DATA_PROP, MPV_FORMAT_NODE, ctx->node); - } - mpv_observe_property(handle, 0, "window-id", MPV_FORMAT_INT64); mpv_observe_property(handle, 0, MENU_DATA_PROP, MPV_FORMAT_NODE); diff --git a/src/plugin.h b/src/plugin.h index ee398a8..4a3dd02 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -10,7 +10,6 @@ #include "misc/dispatch.h" typedef struct { - bool load; // load menu on startup, default: yes bool uosc; // use uosc menu syntax, default: no } plugin_config;