mirror of
https://github.com/mpv-player/mpv
synced 2024-11-14 22:48:35 +01:00
vo: add win32 context menu support
This commit is contained in:
parent
f974382ca0
commit
3c1e983351
2
DOCS/interface-changes/add-win32-context-menu.txt
Normal file
2
DOCS/interface-changes/add-win32-context-menu.txt
Normal file
@ -0,0 +1,2 @@
|
||||
add `context-menu` command
|
||||
add `menu-data` property
|
@ -927,6 +927,9 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
|
||||
<keep-selection>
|
||||
Do not change current track selections.
|
||||
|
||||
``context-menu``
|
||||
Show context menu on the video window. See `Context Menu`_ section for details.
|
||||
|
||||
|
||||
Input Commands that are Possibly Subject to Change
|
||||
--------------------------------------------------
|
||||
@ -3454,6 +3457,48 @@ Property list
|
||||
and not using raw mode, the underlying content will be given (e.g. strings will be
|
||||
printed directly, rather than quoted and JSON-escaped).
|
||||
|
||||
``menu-data`` (RW)
|
||||
This property stores the raw menu definition. See `Context Menu`_ section for details.
|
||||
|
||||
``type``
|
||||
Menu item type. Can be: ``separator``, ``submenu``, or empty.
|
||||
|
||||
``title``
|
||||
Menu item title. Required if type is not ``separator``.
|
||||
|
||||
``cmd``
|
||||
Command to execute when the menu item is clicked.
|
||||
|
||||
``shortcut``
|
||||
Menu item shortcut key which appears to the right of the menu item.
|
||||
A shortcut key does not have to be functional; it's just a visual hint.
|
||||
|
||||
``state``
|
||||
Menu item state. Can be: ``checked``, ``disabled``, ``hidden``, or empty.
|
||||
|
||||
``submenu``
|
||||
Submenu items, which is required if type is ``submenu``.
|
||||
|
||||
When querying the property with the client API using ``MPV_FORMAT_NODE``, or with
|
||||
Lua ``mp.get_property_native``, this will return a mpv_node with the following
|
||||
contents:
|
||||
|
||||
::
|
||||
|
||||
MPV_FORMAT_NODE_ARRAY
|
||||
MPV_FORMAT_NODE_MAP (menu item)
|
||||
"type" MPV_FORMAT_STRING
|
||||
"title" MPV_FORMAT_STRING
|
||||
"cmd" MPV_FORMAT_STRING
|
||||
"shortcut" MPV_FORMAT_STRING
|
||||
"state" MPV_FORMAT_NODE_ARRAY[MPV_FORMAT_STRING]
|
||||
"submenu" MPV_FORMAT_NODE_ARRAY[menu item]
|
||||
|
||||
Writing to this property with the client API using ``MPV_FORMAT_NODE`` or with
|
||||
Lua ``mp.set_property_native`` will trigger an immediate update of the menu if
|
||||
mpv video output is currently active. You may observe the ``current-vo``
|
||||
property to check if this is the case.
|
||||
|
||||
``working-directory``
|
||||
The working directory of the mpv process. Can be useful for JSON IPC users,
|
||||
because the command line player usually works with relative paths.
|
||||
|
@ -310,6 +310,21 @@ Wheel left/right
|
||||
Ctrl+Wheel up/down
|
||||
Change video zoom.
|
||||
|
||||
Context Menu
|
||||
-------------
|
||||
|
||||
.. warning::
|
||||
|
||||
This feature is experimental. It may not work with all VOs. A libass based
|
||||
fallback may be implemented in the future.
|
||||
|
||||
Context Menu is a menu that pops up on the video window on user interaction
|
||||
(mouse right click, etc.).
|
||||
|
||||
To use this feature, you need to fill the ``menu-data`` property with menu
|
||||
definition data, and add a keybinding to run the ``context-menu`` command,
|
||||
which can be done with a user script.
|
||||
|
||||
USAGE
|
||||
=====
|
||||
|
||||
|
@ -503,7 +503,8 @@ if features['win32-desktop']
|
||||
'osdep/terminal-win.c',
|
||||
'video/out/w32_common.c',
|
||||
'video/out/win32/displayconfig.c',
|
||||
'video/out/win32/droptarget.c')
|
||||
'video/out/win32/droptarget.c',
|
||||
'video/out/win32/menu.c')
|
||||
main_fn_source = files('osdep/main-fn-win.c')
|
||||
endif
|
||||
|
||||
|
@ -114,6 +114,7 @@ struct command_ctx {
|
||||
|
||||
char **script_props;
|
||||
mpv_node udata;
|
||||
mpv_node mdata;
|
||||
|
||||
double cached_window_scale;
|
||||
};
|
||||
@ -126,6 +127,10 @@ static const struct m_option udata_type = {
|
||||
.type = CONF_TYPE_NODE
|
||||
};
|
||||
|
||||
static const struct m_option mdata_type = {
|
||||
.type = CONF_TYPE_NODE
|
||||
};
|
||||
|
||||
struct overlay {
|
||||
struct mp_image *source;
|
||||
int x, y;
|
||||
@ -3730,6 +3735,35 @@ static int mp_property_bindings(void *ctx, struct m_property *prop,
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static int mp_property_mdata(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
mpv_node *node = &mpctx->command_ctx->mdata;
|
||||
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET_TYPE:
|
||||
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE};
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_GET:
|
||||
case M_PROPERTY_GET_NODE:
|
||||
m_option_copy(&mdata_type, arg, node);
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_SET:
|
||||
case M_PROPERTY_SET_NODE: {
|
||||
m_option_copy(&mdata_type, node, arg);
|
||||
talloc_steal(mpctx->command_ctx, node_get_alloc(node));
|
||||
mp_notify_property(mpctx, prop->name);
|
||||
|
||||
struct vo *vo = mpctx->video_out;
|
||||
if (vo)
|
||||
vo_control(vo, VOCTRL_UPDATE_MENU, arg);
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static int do_list_udata(int item, int action, void *arg, void *ctx);
|
||||
|
||||
struct udata_ctx {
|
||||
@ -4109,6 +4143,8 @@ static const struct m_property mp_properties_base[] = {
|
||||
{"command-list", mp_property_commands},
|
||||
{"input-bindings", mp_property_bindings},
|
||||
|
||||
{"menu-data", mp_property_mdata},
|
||||
|
||||
{"user-data", mp_property_udata},
|
||||
{"term-size", mp_property_term_size},
|
||||
|
||||
@ -6567,6 +6603,16 @@ static void cmd_begin_vo_dragging(void *p)
|
||||
vo_control(vo, VOCTRL_BEGIN_DRAGGING, NULL);
|
||||
}
|
||||
|
||||
static void cmd_context_menu(void *p)
|
||||
{
|
||||
struct mp_cmd_ctx *cmd = p;
|
||||
struct MPContext *mpctx = cmd->mpctx;
|
||||
struct vo *vo = mpctx->video_out;
|
||||
|
||||
if (vo)
|
||||
vo_control(vo, VOCTRL_SHOW_MENU, NULL);
|
||||
}
|
||||
|
||||
/* This array defines all known commands.
|
||||
* The first field the command name used in libmpv and input.conf.
|
||||
* The second field is the handler function (see mp_cmd_def.handler and
|
||||
@ -7039,6 +7085,8 @@ const struct mp_cmd_def mp_cmds[] = {
|
||||
|
||||
{ "begin-vo-dragging", cmd_begin_vo_dragging },
|
||||
|
||||
{ "context-menu", cmd_context_menu },
|
||||
|
||||
{0}
|
||||
};
|
||||
|
||||
@ -7123,6 +7171,9 @@ void command_init(struct MPContext *mpctx)
|
||||
ctx->properties[count++] = prop;
|
||||
}
|
||||
|
||||
node_init(&ctx->mdata, MPV_FORMAT_NODE_ARRAY, NULL);
|
||||
talloc_steal(ctx, ctx->mdata.u.list);
|
||||
|
||||
node_init(&ctx->udata, MPV_FORMAT_NODE_MAP, NULL);
|
||||
talloc_steal(ctx, ctx->udata.u.list);
|
||||
talloc_free(prop_names);
|
||||
|
@ -125,6 +125,10 @@ enum mp_voctrl {
|
||||
|
||||
// Begin VO dragging.
|
||||
VOCTRL_BEGIN_DRAGGING,
|
||||
|
||||
// Native context menu
|
||||
VOCTRL_SHOW_MENU,
|
||||
VOCTRL_UPDATE_MENU,
|
||||
};
|
||||
|
||||
// Helper to expose what kind of content is currently playing to the VO.
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "w32_common.h"
|
||||
#include "win32/displayconfig.h"
|
||||
#include "win32/droptarget.h"
|
||||
#include "win32/menu.h"
|
||||
#include "osdep/io.h"
|
||||
#include "osdep/threads.h"
|
||||
#include "osdep/w32_keyboard.h"
|
||||
@ -82,6 +83,8 @@ typedef enum MONITOR_DPI_TYPE {
|
||||
#define rect_w(r) ((r).right - (r).left)
|
||||
#define rect_h(r) ((r).bottom - (r).top)
|
||||
|
||||
#define WM_SHOWMENU (WM_USER + 1)
|
||||
|
||||
struct w32_api {
|
||||
HRESULT (WINAPI *pGetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
|
||||
BOOL (WINAPI *pAdjustWindowRectExForDpi)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi);
|
||||
@ -109,6 +112,8 @@ struct vo_w32_state {
|
||||
HHOOK parent_win_hook;
|
||||
HWINEVENTHOOK parent_evt_hook;
|
||||
|
||||
struct menu_ctx *menu_ctx;
|
||||
|
||||
HMONITOR monitor; // Handle of the current screen
|
||||
char *color_profile; // Path of the current screen's color profile
|
||||
|
||||
@ -1486,6 +1491,14 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
|
||||
w32->window = NULL;
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
case WM_COMMAND: {
|
||||
const char *cmd = mp_win32_menu_get_cmd(w32->menu_ctx, LOWORD(wParam));
|
||||
if (cmd) {
|
||||
mp_cmd_t *cmdt = mp_input_parse_cmd(w32->input_ctx, bstr0(cmd), "");
|
||||
mp_input_queue_cmd(w32->input_ctx, cmdt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_SYSCOMMAND:
|
||||
switch (wParam & 0xFFF0) {
|
||||
case SC_SCREENSAVE:
|
||||
@ -1687,6 +1700,9 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_SHOWMENU:
|
||||
mp_win32_menu_show(w32->menu_ctx, w32->window);
|
||||
break;
|
||||
}
|
||||
|
||||
if (message == w32->tbtn_created_msg) {
|
||||
@ -2060,6 +2076,7 @@ bool vo_w32_init(struct vo *vo)
|
||||
.dispatch = mp_dispatch_create(w32),
|
||||
};
|
||||
w32->opts = w32->opts_cache->opts;
|
||||
w32->menu_ctx = mp_win32_menu_init();
|
||||
vo->w32 = w32;
|
||||
|
||||
if (mp_thread_create(&w32->thread, gui_thread, w32))
|
||||
@ -2272,6 +2289,12 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
|
||||
case VOCTRL_BEGIN_DRAGGING:
|
||||
w32->start_dragging = true;
|
||||
return VO_TRUE;
|
||||
case VOCTRL_SHOW_MENU:
|
||||
PostMessageW(w32->window, WM_SHOWMENU, 0, 0);
|
||||
return VO_TRUE;
|
||||
case VOCTRL_UPDATE_MENU:
|
||||
mp_win32_menu_update(w32->menu_ctx, (struct mpv_node *)arg);
|
||||
return VO_TRUE;
|
||||
}
|
||||
return VO_NOTIMPL;
|
||||
}
|
||||
@ -2335,6 +2358,7 @@ void vo_w32_uninit(struct vo *vo)
|
||||
|
||||
AvRevertMmThreadCharacteristics(w32->avrt_handle);
|
||||
|
||||
mp_win32_menu_uninit(w32->menu_ctx);
|
||||
talloc_free(w32);
|
||||
vo->w32 = NULL;
|
||||
}
|
||||
|
231
video/out/win32/menu.c
Normal file
231
video/out/win32/menu.c
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libmpv/client.h"
|
||||
#include "osdep/io.h"
|
||||
#include "mpv_talloc.h"
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
struct menu_ctx {
|
||||
HMENU menu;
|
||||
void *ta_data; // talloc context for MENUITEMINFOW.dwItemData
|
||||
};
|
||||
|
||||
// append menu item to HMENU
|
||||
static int append_menu(HMENU hmenu, UINT fMask, UINT fType, UINT fState,
|
||||
wchar_t *title, HMENU submenu, void *data)
|
||||
{
|
||||
static UINT id = WM_USER + 100;
|
||||
MENUITEMINFOW mii = {0};
|
||||
|
||||
mii.cbSize = sizeof(mii);
|
||||
mii.fMask = MIIM_ID | fMask;
|
||||
mii.wID = id++;
|
||||
|
||||
if (fMask & MIIM_FTYPE)
|
||||
mii.fType = fType;
|
||||
if (fMask & MIIM_STATE)
|
||||
mii.fState = fState;
|
||||
if (fMask & MIIM_STRING) {
|
||||
mii.dwTypeData = title;
|
||||
mii.cch = wcslen(title);
|
||||
}
|
||||
if (fMask & MIIM_SUBMENU)
|
||||
mii.hSubMenu = submenu;
|
||||
if (fMask & MIIM_DATA)
|
||||
mii.dwItemData = (ULONG_PTR)data;
|
||||
|
||||
return InsertMenuItemW(hmenu, -1, TRUE, &mii) ? mii.wID : -1;
|
||||
}
|
||||
|
||||
// build fState for menu item creation
|
||||
static int build_state(mpv_node *node)
|
||||
{
|
||||
int fState = 0;
|
||||
for (int i = 0; i < node->u.list->num; i++) {
|
||||
mpv_node *item = &node->u.list->values[i];
|
||||
if (item->format != MPV_FORMAT_STRING)
|
||||
continue;
|
||||
|
||||
if (strcmp(item->u.string, "hidden") == 0) {
|
||||
return -1;
|
||||
} else if (strcmp(item->u.string, "checked") == 0) {
|
||||
fState |= MFS_CHECKED;
|
||||
} else if (strcmp(item->u.string, "disabled") == 0) {
|
||||
fState |= MFS_DISABLED;
|
||||
}
|
||||
}
|
||||
return fState;
|
||||
}
|
||||
|
||||
// build dwTypeData for menu item creation
|
||||
static wchar_t *build_title(void *talloc_ctx, char *title, char *shortcut)
|
||||
{
|
||||
if (shortcut && shortcut[0]) {
|
||||
char *buf = talloc_asprintf(NULL, "%s\t%s", title, shortcut);
|
||||
wchar_t *wbuf = mp_from_utf8(talloc_ctx, buf);
|
||||
talloc_free(buf);
|
||||
return wbuf;
|
||||
}
|
||||
return mp_from_utf8(talloc_ctx, title);
|
||||
}
|
||||
|
||||
// build HMENU from mpv node
|
||||
//
|
||||
// node structure:
|
||||
//
|
||||
// MPV_FORMAT_NODE_ARRAY
|
||||
// MPV_FORMAT_NODE_MAP (menu item)
|
||||
// "type" MPV_FORMAT_STRING
|
||||
// "title" MPV_FORMAT_STRING
|
||||
// "cmd" MPV_FORMAT_STRING
|
||||
// "shortcut" MPV_FORMAT_STRING
|
||||
// "state" MPV_FORMAT_NODE_ARRAY[MPV_FORMAT_STRING]
|
||||
// "submenu" MPV_FORMAT_NODE_ARRAY[menu item]
|
||||
static void build_menu(void *talloc_ctx, HMENU hmenu, struct mpv_node *node)
|
||||
{
|
||||
if (node->format != MPV_FORMAT_NODE_ARRAY)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < node->u.list->num; i++) {
|
||||
mpv_node *item = &node->u.list->values[i];
|
||||
if (item->format != MPV_FORMAT_NODE_MAP)
|
||||
continue;
|
||||
|
||||
mpv_node_list *list = item->u.list;
|
||||
|
||||
char *type = "";
|
||||
char *title = NULL;
|
||||
char *cmd = NULL;
|
||||
char *shortcut = NULL;
|
||||
int fState = 0;
|
||||
HMENU submenu = NULL;
|
||||
|
||||
for (int j = 0; j < list->num; j++) {
|
||||
char *key = list->keys[j];
|
||||
mpv_node *value = &list->values[j];
|
||||
|
||||
switch (value->format) {
|
||||
case MPV_FORMAT_STRING:
|
||||
if (strcmp(key, "title") == 0) {
|
||||
title = value->u.string;
|
||||
} else if (strcmp(key, "cmd") == 0) {
|
||||
cmd = value->u.string;
|
||||
} else if (strcmp(key, "type") == 0) {
|
||||
type = value->u.string;
|
||||
} else if (strcmp(key, "shortcut") == 0) {
|
||||
shortcut = value->u.string;
|
||||
}
|
||||
break;
|
||||
case MPV_FORMAT_NODE_ARRAY:
|
||||
if (strcmp(key, "state") == 0) {
|
||||
fState = build_state(value);
|
||||
} else if (strcmp(key, "submenu") == 0) {
|
||||
submenu = CreatePopupMenu();
|
||||
build_menu(talloc_ctx, submenu, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fState == -1) // hidden
|
||||
continue;
|
||||
|
||||
if (strcmp(type, "separator") == 0) {
|
||||
append_menu(hmenu, MIIM_FTYPE, MFT_SEPARATOR, 0, NULL, NULL, NULL);
|
||||
} else {
|
||||
if (title == NULL || title[0] == '\0')
|
||||
continue;
|
||||
|
||||
UINT fMask = MIIM_STRING | MIIM_STATE;
|
||||
bool grayed = false;
|
||||
if (strcmp(type, "submenu") == 0) {
|
||||
if (submenu == NULL)
|
||||
submenu = CreatePopupMenu();
|
||||
fMask |= MIIM_SUBMENU;
|
||||
grayed = GetMenuItemCount(submenu) == 0;
|
||||
} else {
|
||||
fMask |= MIIM_DATA;
|
||||
grayed = cmd == NULL || cmd[0] == '\0' || cmd[0] == '#' ||
|
||||
strcmp(cmd, "ignore") == 0;
|
||||
}
|
||||
int id = append_menu(hmenu, fMask, 0, (UINT)fState,
|
||||
build_title(talloc_ctx, title, shortcut),
|
||||
submenu, talloc_strdup(talloc_ctx, cmd));
|
||||
if (id > 0 && grayed)
|
||||
EnableMenuItem(hmenu, id, MF_BYCOMMAND | MF_GRAYED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct menu_ctx *mp_win32_menu_init(void)
|
||||
{
|
||||
struct menu_ctx *ctx = talloc_ptrtype(NULL, ctx);
|
||||
ctx->menu = CreatePopupMenu();
|
||||
ctx->ta_data = talloc_new(ctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void mp_win32_menu_uninit(struct menu_ctx *ctx)
|
||||
{
|
||||
DestroyMenu(ctx->menu);
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
void mp_win32_menu_show(struct menu_ctx *ctx, HWND hwnd)
|
||||
{
|
||||
POINT pt;
|
||||
RECT rc;
|
||||
|
||||
if (!GetCursorPos(&pt))
|
||||
return;
|
||||
|
||||
GetClientRect(hwnd, &rc);
|
||||
ScreenToClient(hwnd, &pt);
|
||||
|
||||
if (!PtInRect(&rc, pt))
|
||||
return;
|
||||
|
||||
ClientToScreen(hwnd, &pt);
|
||||
TrackPopupMenuEx(ctx->menu, TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y,
|
||||
hwnd, NULL);
|
||||
}
|
||||
|
||||
void mp_win32_menu_update(struct menu_ctx *ctx, struct mpv_node *data)
|
||||
{
|
||||
while (GetMenuItemCount(ctx->menu) > 0)
|
||||
RemoveMenu(ctx->menu, 0, MF_BYPOSITION);
|
||||
talloc_free_children(ctx->ta_data);
|
||||
|
||||
build_menu(ctx->ta_data, ctx->menu, data);
|
||||
}
|
||||
|
||||
const char* mp_win32_menu_get_cmd(struct menu_ctx *ctx, UINT id)
|
||||
{
|
||||
MENUITEMINFOW mii = {0};
|
||||
mii.cbSize = sizeof(mii);
|
||||
mii.fMask = MIIM_DATA;
|
||||
|
||||
GetMenuItemInfoW(ctx->menu, id, FALSE, &mii);
|
||||
return (const char *)mii.dwItemData;
|
||||
}
|
32
video/out/win32/menu.h
Normal file
32
video/out/win32/menu.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MP_WIN32_MENU_H
|
||||
#define MP_WIN32_MENU_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
struct mpv_node;
|
||||
struct menu_ctx;
|
||||
|
||||
struct menu_ctx *mp_win32_menu_init(void);
|
||||
void mp_win32_menu_uninit(struct menu_ctx *ctx);
|
||||
void mp_win32_menu_show(struct menu_ctx *ctx, HWND hwnd);
|
||||
void mp_win32_menu_update(struct menu_ctx *ctx, struct mpv_node *data);
|
||||
const char* mp_win32_menu_get_cmd(struct menu_ctx *ctx, UINT id);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user