Skip to content

Commit

Permalink
add dialog interface
Browse files Browse the repository at this point in the history
  • Loading branch information
tsl0922 committed Jan 19, 2024
1 parent 637b795 commit 8f29aa3
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 1 deletion.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_library(menu SHARED
mpv/ta/ta_talloc.c
mpv/ta/ta_utils.c

src/dialog.c
src/menu.c
src/plugin.c
)
Expand Down
228 changes: 228 additions & 0 deletions src/dialog.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#include <windows.h>
#include <shobjidl.h>
#include <mpv/client.h>
#include "mpv_talloc.h"
#include "dialog.h"

#define DIALOG_FILTER_PROP "user-data/menu/dialog/filters"
#define DIALOG_DEF_PATH_PROP "user-data/menu/dialog/default-path"
#define DIALOG_DEF_NAME_PROP "user-data/menu/dialog/default-name"

// add filters to dialog from user property, default to first one
static void add_filters(mpv_handle *mpv, IFileDialog *pfd) {
mpv_node node = {0};
if (mpv_get_property(mpv, DIALOG_FILTER_PROP, MPV_FORMAT_NODE, &node) < 0)
return;
if (node.format != MPV_FORMAT_NODE_ARRAY) goto done;

void *tmp = talloc_new(NULL);
mpv_node_list *list = node.u.list;
COMDLG_FILTERSPEC *specs = talloc_array(tmp, COMDLG_FILTERSPEC, list->num);
UINT count = 0;

for (int i = 0; i < list->num; i++) {
mpv_node *item = &list->values[i];
if (item->format != MPV_FORMAT_NODE_MAP) continue;

char *name = NULL, *spec = NULL;
mpv_node_list *values = item->u.list;

for (int j = 0; j < values->num; j++) {
char *key = values->keys[j];
mpv_node *value = &values->values[j];
if (value->format != MPV_FORMAT_STRING) continue;

if (strcmp(key, "name") == 0) name = value->u.string;
if (strcmp(key, "spec") == 0) spec = value->u.string;
}

if (name != NULL && spec != NULL) {
specs[count].pszName = mp_from_utf8(tmp, name);
specs[count].pszSpec = mp_from_utf8(tmp, spec);
count++;
}
}

if (count > 0) {
pfd->lpVtbl->SetFileTypes(pfd, count, specs);
pfd->lpVtbl->SetFileTypeIndex(pfd, 1);
pfd->lpVtbl->SetDefaultExtension(pfd, specs[0].pszSpec);
}

talloc_free(tmp);

done:
mpv_free_node_contents(&node);
}

// set default path from user property
static void set_default_path(mpv_handle *mpv, IFileDialog *pfd) {
char *path = mpv_get_property_string(mpv, DIALOG_DEF_PATH_PROP);
if (path == NULL) return;

IShellItem *folder;
wchar_t *w_path = mp_from_utf8(NULL, path);

if (SUCCEEDED(SHCreateItemFromParsingName(w_path, NULL, &IID_IShellItem,
(void **)&folder)))
pfd->lpVtbl->SetDefaultFolder(pfd, folder);

talloc_free(w_path);
mpv_free(path);
}

// set default name used for save dialog
static void set_default_name(mpv_handle *mpv, IFileDialog *pfd) {
char *name = mpv_get_property_string(mpv, DIALOG_DEF_NAME_PROP);
if (name == NULL) return;

wchar_t *w_name = mp_from_utf8(NULL, name);
pfd->lpVtbl->SetFileName(pfd, w_name);
talloc_free(w_name);
}

static void add_options(IFileDialog *pfd, DWORD options) {
DWORD dwOptions;
if (pfd->lpVtbl->GetOptions(pfd, &dwOptions) == S_OK) {
pfd->lpVtbl->SetOptions(pfd, dwOptions | options);
}
}

// single file open dialog
char *open_dialog(void *talloc_ctx, plugin_ctx *ctx) {
IFileOpenDialog *pfd = NULL;
if (FAILED(CoCreateInstance(&CLSID_FileOpenDialog, NULL,
CLSCTX_INPROC_SERVER, &IID_IFileDialog,
(void **)&pfd)))
return NULL;

add_filters(ctx->mpv, (IFileDialog *)pfd);
set_default_path(ctx->mpv, (IFileDialog *)pfd);
add_options((IFileDialog *)pfd, FOS_FORCEFILESYSTEM);

char *path = NULL;

if (SUCCEEDED(pfd->lpVtbl->Show(pfd, ctx->hwnd))) {
IShellItem *psi;
if (SUCCEEDED(pfd->lpVtbl->GetResult(pfd, &psi))) {
wchar_t *w_path;
if (SUCCEEDED(psi->lpVtbl->GetDisplayName(psi, SIGDN_FILESYSPATH,
&w_path))) {
path = mp_to_utf8(talloc_ctx, w_path);
CoTaskMemFree(w_path);
}
psi->lpVtbl->Release(psi);
}
}

pfd->lpVtbl->Release(pfd);

return path;
}

// multiple file open dialog
char **open_dialog_multiple(void *talloc_ctx, plugin_ctx *ctx) {
IFileOpenDialog *pfd = NULL;
if (FAILED(CoCreateInstance(&CLSID_FileOpenDialog, NULL,
CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog,
(void **)&pfd)))
return NULL;

add_filters(ctx->mpv, (IFileDialog *)pfd);
set_default_path(ctx->mpv, (IFileDialog *)pfd);
add_options((IFileDialog *)pfd, FOS_FORCEFILESYSTEM | FOS_ALLOWMULTISELECT);

char **paths = NULL;

if (SUCCEEDED(pfd->lpVtbl->Show(pfd, ctx->hwnd))) {
IShellItemArray *psia;
if (SUCCEEDED(pfd->lpVtbl->GetResults(pfd, &psia))) {
DWORD count;
if (SUCCEEDED(psia->lpVtbl->GetCount(psia, &count))) {
paths =
talloc_zero_size(talloc_ctx, sizeof(char *) * (count + 1));
for (DWORD i = 0; i < count; i++) {
IShellItem *psi;
if (SUCCEEDED(psia->lpVtbl->GetItemAt(psia, i, &psi))) {
wchar_t *w_path;
if (SUCCEEDED(psi->lpVtbl->GetDisplayName(
psi, SIGDN_FILESYSPATH, &w_path))) {
paths[i] = mp_to_utf8(talloc_ctx, w_path);
CoTaskMemFree(w_path);
}
psi->lpVtbl->Release(psi);
}
}
}
psia->lpVtbl->Release(psia);
}
}

pfd->lpVtbl->Release(pfd);

return paths;
}

// folder open dialog
char *open_folder(void *talloc_ctx, plugin_ctx *ctx) {
IFileOpenDialog *pfd = NULL;
if (FAILED(CoCreateInstance(&CLSID_FileOpenDialog, NULL,
CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog,
(void **)&pfd)))
return NULL;

set_default_path(ctx->mpv, (IFileDialog *)pfd);
add_options((IFileDialog *)pfd, FOS_FORCEFILESYSTEM | FOS_PICKFOLDERS);

char *path = NULL;

if (SUCCEEDED(pfd->lpVtbl->Show(pfd, ctx->hwnd))) {
IShellItem *psi;
if (SUCCEEDED(pfd->lpVtbl->GetResult(pfd, &psi))) {
wchar_t *w_path;
if (SUCCEEDED(psi->lpVtbl->GetDisplayName(psi, SIGDN_FILESYSPATH,
&w_path))) {
path = mp_to_utf8(talloc_ctx, w_path);
CoTaskMemFree(w_path);
}
psi->lpVtbl->Release(psi);
}
}

pfd->lpVtbl->Release(pfd);

return path;
}

// save dialog
char *save_dialog(void *talloc_ctx, plugin_ctx *ctx) {
IFileSaveDialog *pfd = NULL;
if (FAILED(CoCreateInstance(&CLSID_FileSaveDialog, NULL,
CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog,
(void **)&pfd)))
return NULL;

add_filters(ctx->mpv, (IFileDialog *)pfd);
set_default_path(ctx->mpv, (IFileDialog *)pfd);
set_default_name(ctx->mpv, (IFileDialog *)pfd);
add_options((IFileDialog *)pfd, FOS_FORCEFILESYSTEM);

char *path = NULL;

if (SUCCEEDED(pfd->lpVtbl->Show(pfd, ctx->hwnd))) {
IShellItem *psi;
if (SUCCEEDED(pfd->lpVtbl->GetResult(pfd, &psi))) {
wchar_t *w_path;
if (SUCCEEDED(psi->lpVtbl->GetDisplayName(psi, SIGDN_FILESYSPATH,
&w_path))) {
path = mp_to_utf8(talloc_ctx, w_path);
CoTaskMemFree(w_path);
}
psi->lpVtbl->Release(psi);
}
}

pfd->lpVtbl->Release(pfd);

return path;
}
13 changes: 13 additions & 0 deletions src/dialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2023 tsl0922. All rights reserved.
// SPDX-License-Identifier: GPL-2.0-only

#ifndef MPV_PLUGIN_DIALOG_H
#define MPV_PLUGIN_DIALOG_H

#include "plugin.h"

char *open_dialog(void *talloc_ctx, plugin_ctx *ctx);
char **open_dialog_multiple(void *talloc_ctx, plugin_ctx *ctx);
char *open_folder(void *talloc_ctx, plugin_ctx *ctx);
char *save_dialog(void *talloc_ctx, plugin_ctx *ctx);
#endif
59 changes: 58 additions & 1 deletion src/plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

#include <windows.h>
#include <windowsx.h>
#include <objbase.h>
#include <shlwapi.h>
#include <mpv/client.h>
#include "misc/bstr.h"
#include "misc/ctype.h"
#include "dialog.h"
#include "menu.h"
#include "plugin.h"

Expand Down Expand Up @@ -93,7 +95,49 @@ static void handle_client_message(mpv_event *event) {
if (msg->num_args < 1) return;

const char *cmd = msg->args[0];
if (strcmp(cmd, "show") == 0) PostMessageW(ctx->hwnd, WM_SHOWMENU, 0, 0);
if (strcmp(cmd, "show") == 0) {
PostMessageW(ctx->hwnd, WM_SHOWMENU, 0, 0);
} else if (strcmp(cmd, "dialog/open") == 0 && msg->num_args > 1) {
char *path = open_dialog(NULL, ctx);
if (path != NULL) {
mpv_command(ctx->mpv,
(const char *[]){"script-message-to", msg->args[1],
"dialog-open-reply", path, NULL});
talloc_free(path);
}
} else if (strcmp(cmd, "dialog/open-multi") == 0 && msg->num_args > 1) {
void *tmp = talloc_new(NULL);
char **paths = open_dialog_multiple(tmp, ctx);
if (paths != NULL) {
int count = 0;
while (paths[count] != NULL) count++;

char **args = talloc_array(tmp, char *, count + 4);
args[0] = talloc_strdup(tmp, "script-message-to");
args[1] = talloc_strdup(tmp, msg->args[1]);
args[2] = talloc_strdup(tmp, "dialog-open-multi-reply");
for (int i = 0; i < count; i++) args[i + 3] = paths[i];
args[count + 3] = NULL;
mpv_command(ctx->mpv, (const char **)args);
}
talloc_free(tmp);
} else if (strcmp(cmd, "dialog/open-folder") == 0 && msg->num_args > 1) {
char *path = open_folder(NULL, ctx);
if (path != NULL) {
mpv_command(ctx->mpv, (const char *[]){
"script-message-to", msg->args[1],
"dialog-open-folder-reply", path, NULL});
talloc_free(path);
}
} else if (strcmp(cmd, "dialog/save") == 0 && msg->num_args > 1) {
char *path = save_dialog(NULL, ctx);
if (path != NULL) {
mpv_command(ctx->mpv,
(const char *[]){"script-message-to", msg->args[1],
"dialog-save-reply", path, NULL});
talloc_free(path);
}
}
}

// create and init plugin context
Expand All @@ -119,6 +163,8 @@ static void destroy_plugin_ctx() {

// entry point of plugin
MPV_EXPORT int mpv_open_cplugin(mpv_handle *handle) {
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

create_plugin_ctx(handle);

if (ctx->conf->load) {
Expand Down Expand Up @@ -150,6 +196,8 @@ MPV_EXPORT int mpv_open_cplugin(mpv_handle *handle) {
mpv_unobserve_property(handle, 0);
destroy_plugin_ctx();

CoUninitialize();

return 0;
}

Expand All @@ -162,6 +210,15 @@ wchar_t *mp_from_utf8(void *talloc_ctx, const char *s) {
return ret;
}

// convert wchar_t string to utf8
char *mp_to_utf8(void *talloc_ctx, const wchar_t *s) {
int count = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
if (count <= 0) abort();
char *ret = talloc_array(talloc_ctx, char, count);
WideCharToMultiByte(CP_UTF8, 0, s, -1, ret, count, NULL, NULL);
return ret;
}

// get property value as string
char *mp_get_prop_string(void *talloc_ctx, const char *prop) {
char *val = mpv_get_property_string(ctx->mpv, prop);
Expand Down
2 changes: 2 additions & 0 deletions src/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#ifndef MPV_PLUGIN_H
#define MPV_PLUGIN_H

#include <stdbool.h>
#include <windows.h>
#include <mpv/client.h>
#include "misc/dispatch.h"
Expand All @@ -27,6 +28,7 @@ typedef struct {
} plugin_ctx;

wchar_t *mp_from_utf8(void *talloc_ctx, const char *s);
char *mp_to_utf8(void *talloc_ctx, const wchar_t *s);
char *mp_get_prop_string(void *talloc_ctx, const char *prop);
char *mp_expand_path(void *talloc_ctx, char *path);
char *mp_read_file(void *talloc_ctx, char *path);
Expand Down

0 comments on commit 8f29aa3

Please sign in to comment.