summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
authorShuanglei Tao <tsl0922@gmail.com>2024-03-15 00:06:58 +0800
committerKacper Michajłow <kasper93@gmail.com>2024-04-06 08:24:06 +0200
commit3c1e98335127b28a7dc1b194e832b0e422f5ec9a (patch)
treed4fb1eb96416cfa9bae6f58cf3f15d6aaa6943ec /video/out
parentf974382ca06655ac34debce7284ce87d01e5abd1 (diff)
downloadmpv-3c1e98335127b28a7dc1b194e832b0e422f5ec9a.tar.bz2
mpv-3c1e98335127b28a7dc1b194e832b0e422f5ec9a.tar.xz
vo: add win32 context menu support
Diffstat (limited to 'video/out')
-rw-r--r--video/out/vo.h4
-rw-r--r--video/out/w32_common.c24
-rw-r--r--video/out/win32/menu.c231
-rw-r--r--video/out/win32/menu.h32
4 files changed, 291 insertions, 0 deletions
diff --git a/video/out/vo.h b/video/out/vo.h
index eb92d4fc5b..313c08aafc 100644
--- a/video/out/vo.h
+++ b/video/out/vo.h
@@ -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.
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index 66d1fc4701..36f48b9be7 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -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;
}
diff --git a/video/out/win32/menu.c b/video/out/win32/menu.c
new file mode 100644
index 0000000000..25681e8ae3
--- /dev/null
+++ b/video/out/win32/menu.c
@@ -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;
+}
diff --git a/video/out/win32/menu.h b/video/out/win32/menu.h
new file mode 100644
index 0000000000..8b1fe72c19
--- /dev/null
+++ b/video/out/win32/menu.h
@@ -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