Skip to content

Commit

Permalink
move 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 ac04767
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 206 deletions.
1 change: 0 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,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
105 changes: 0 additions & 105 deletions src/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
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
68 changes: 0 additions & 68 deletions src/plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,30 +99,6 @@ static void set_clipboard(const char *text) {
CloseClipboard();
}

// read and parse config file
static void read_conf(plugin_config *conf, const char *name) {
void *tmp = talloc_new(NULL);
char *path = talloc_asprintf(tmp, "~~/script-opts/%s.conf", name);
bstr data = bstr0(mp_read_file(tmp, path));

while (data.len) {
bstr line = bstr_strip_linebreaks(bstr_getline(data, &data));
line = bstr_lstrip(line);
if (line.len == 0 || bstr_startswith0(line, "#")) continue;

bstr name, value;
if (!bstr_split_tok(line, "=", &name, &value)) continue;
name = bstr_strip(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");
}

talloc_free(tmp);
}

// create HMENU and register window procedure
static void plugin_register(int64_t wid) {
ctx->hmenu = CreatePopupMenu();
Expand Down Expand Up @@ -226,10 +202,6 @@ static void create_plugin_ctx(mpv_handle *mpv) {
ctx = talloc_zero(NULL, plugin_ctx);
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);
ctx->node = talloc_zero(ctx, mpv_node);
}
Expand All @@ -248,11 +220,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);

Expand Down Expand Up @@ -308,17 +275,6 @@ char *mp_get_prop_string(void *talloc_ctx, const char *prop) {
return ret;
}

// wrapper of expand-path command
char *mp_expand_path(void *talloc_ctx, char *path) {
mpv_node node;
const char *args[] = {"expand-path", path, NULL};
if (mpv_command_ret(ctx->mpv, args, &node) >= 0) {
path = talloc_strdup(talloc_ctx, node.u.string);
mpv_free_node_contents(&node);
}
return path;
}

static void async_cmd_fn(void *data) {
mpv_command_string(ctx->mpv, (const char *)data);
}
Expand All @@ -328,27 +284,3 @@ void mp_command_async(const char *args) {
mp_dispatch_enqueue(ctx->dispatch, async_cmd_fn, (void *)args);
mpv_wakeup(ctx->mpv);
}

// read file from disk or memory://
char *mp_read_file(void *talloc_ctx, char *path) {
if (bstr_startswith0(bstr0(path), "memory://"))
return talloc_strdup(talloc_ctx, path + strlen("memory://"));

void *tmp = talloc_new(NULL);
char *path_m = mp_expand_path(tmp, path);
wchar_t *path_w = mp_from_utf8(tmp, path_m);
HANDLE hFile = CreateFileW(path_w, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
talloc_free(tmp);
if (hFile == INVALID_HANDLE_VALUE) return NULL;

DWORD dwFileSize = GetFileSize(hFile, NULL);
char *ret = talloc_array(talloc_ctx, char, dwFileSize + 1);
DWORD dwRead;

ReadFile(hFile, ret, dwFileSize, &dwRead, NULL);
ret[dwFileSize] = '\0';
CloseHandle(hFile);

return ret;
}
Loading

0 comments on commit ac04767

Please sign in to comment.