diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 97f9781d28..991f9734b9 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -3232,8 +3232,7 @@ Property list (key and string value for each pad-btn entry) ``dropped-files`` - Information of the most recent drag-and-drop event mpv received. This - property is only updated when ``--input-builtin-drag-and-drop=no``. + Information of the most recent drag-and-drop event mpv received. A client can observe this property to detect when a drag-and-drop event happens. This property is unavailable if no drag-and-drop event has happened. diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index e0fbb025c4..f530b1f402 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -4454,8 +4454,7 @@ Input ``--input-builtin-drag-and-drop=`` Enable the built-in drag-and-drop behavior (default: yes). Setting it to no - disables the built-in drag-and-drop handling and enables the ``dropped-files`` - property, which can be used to retrieve information about dropped files. + disables the built-in drag-and-drop handling. ``--input-cmdlist`` Prints all commands that can be bound to keys. diff --git a/input/dnd.c b/input/dnd.c new file mode 100644 index 0000000000..2fbc385523 --- /dev/null +++ b/input/dnd.c @@ -0,0 +1,141 @@ +/* + * 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 . + */ + +#include "dnd.h" +#include "common/msg.h" +#include "misc/bstr.h" +#include "misc/node.h" +#include "misc/path_utils.h" +#include "osdep/threads.h" +#include "player/client.h" + +static bool might_be_subtitle_file(mpv_node *sub_exts, char *file) +{ + for (int i = 0; i < sub_exts->u.list->num; i++) { + mpv_node sub_ext = sub_exts->u.list->values[i]; + if (sub_ext.format != MPV_FORMAT_STRING) + continue; + if (!bstrcasecmp0(bstr_get_ext(bstr0(file)), sub_ext.u.string)) + return true; + } + return false; +} + +static void handle_dnd(mpv_handle *mpv, mpv_node *files, char *action) +{ + mpv_node sub_exts = {0}; + if (mpv_get_property(mpv, "sub-auto-exts", MPV_FORMAT_NODE, &sub_exts) != MPV_ERROR_SUCCESS || + sub_exts.format != MPV_FORMAT_NODE_ARRAY) + goto end; + + struct mpv_node_list *list = files->u.list; + for (int i = 0; i < list->num; i++) { + mpv_node file = list->values[i]; + if (file.format != MPV_FORMAT_STRING) + goto end; + } + + bool all_sub = true; + for (int i = 0; i < list->num; i++) + all_sub &= might_be_subtitle_file(&sub_exts, list->values[i].u.string); + + if (all_sub) { + for (int i = 0; i < list->num; i++) { + const char *cmd[] = { + "osd-auto", + "sub-add", + list->values[i].u.string, + NULL + }; + mpv_command(mpv, cmd); + } + } else if (!strcmp(action, "insert-next")) { + /* To insert the entries in the correct order, we iterate over them + backwards */ + for (int i = list->num - 1; i >= 0; i--) { + const char *cmd[] = { + "osd-auto", + "loadfile", + list->values[i].u.string, + /* Since we're inserting in reverse, wait til the final item + is added to start playing */ + (i > 0) ? "insert-next" : "insert-next-play", + NULL + }; + mpv_command(mpv, cmd); + } + } else { + for (int i = 0; i < list->num; i++) { + const char *cmd[] = { + "osd-auto", + "loadfile", + list->values[i].u.string, + /* Either start playing the dropped files right away + or add them to the end of the current playlist */ + (i == 0 && !strcmp(action, "replace")) ? "replace" : "append-play", + NULL + }; + mpv_command(mpv, cmd); + } + } + +end: + mpv_free_node_contents(&sub_exts); +} + +static MP_THREAD_VOID mpv_event_loop_fn(void *arg) +{ + mp_thread_set_name("dnd"); + mpv_handle *mpv = arg; + bool enabled = false; + mpv_observe_property(mpv, 0, "dropped-files", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "input-builtin-drag-and-drop", MPV_FORMAT_FLAG); + + while (1) { + mpv_event *event = mpv_wait_event(mpv, -1); + if (event->event_id == MPV_EVENT_SHUTDOWN) + break; + if (event->event_id == MPV_EVENT_PROPERTY_CHANGE) { + mpv_event_property *prop = event->data; + if (enabled && !strcmp(prop->name, "dropped-files") && prop->format == MPV_FORMAT_NODE) { + mpv_node *node = prop->data; + mpv_node *action = node_map_get(node, "action"); + if (!action || action->format != MPV_FORMAT_STRING) + continue; + + mpv_node *files = node_map_get(node, "files"); + if (!files || files->format != MPV_FORMAT_NODE_ARRAY) + continue; + handle_dnd(mpv, files, action->u.string); + } + if (!strcmp(prop->name, "input-builtin-drag-and-drop") && prop->format == MPV_FORMAT_FLAG) + enabled = *(int *)prop->data; + } + } + + mpv_destroy(mpv); + MP_THREAD_RETURN(); +} + +void mp_dnd_init(mpv_handle *mpv) +{ + mp_thread mpv_event_loop; + if (!mp_thread_create(&mpv_event_loop, mpv_event_loop_fn, mpv)) + mp_thread_detach(mpv_event_loop); + else + mpv_destroy(mpv); +} diff --git a/input/dnd.h b/input/dnd.h new file mode 100644 index 0000000000..8bb9f0ed9d --- /dev/null +++ b/input/dnd.h @@ -0,0 +1,21 @@ +/* + * 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 . + */ +#pragma once + +#include "player/client.h" + +void mp_dnd_init(mpv_handle *mpv); diff --git a/input/input.c b/input/input.c index 93f3a5dcb9..d28d4e231a 100644 --- a/input/input.c +++ b/input/input.c @@ -210,7 +210,6 @@ struct input_opts { bool default_bindings; bool builtin_bindings; bool builtin_dragging; - bool builtin_dnd; bool enable_mouse_movements; bool vo_key_input; bool test; @@ -230,7 +229,6 @@ const struct m_sub_options input_config = { {"input-default-bindings", OPT_BOOL(default_bindings)}, {"input-builtin-bindings", OPT_BOOL(builtin_bindings)}, {"input-builtin-dragging", OPT_BOOL(builtin_dragging)}, - {"input-builtin-drag-and-drop", OPT_BOOL(builtin_dnd)}, {"input-test", OPT_BOOL(test)}, {"input-doubleclick-time", OPT_INT(doubleclick_time), M_RANGE(0, 1000)}, @@ -262,7 +260,6 @@ const struct m_sub_options input_config = { .default_bindings = true, .builtin_bindings = true, .builtin_dragging = true, - .builtin_dnd = true, .vo_key_input = true, .allow_win_drag = true, .preprocess_wheel = true, @@ -1291,63 +1288,15 @@ void mp_input_drop_files(struct input_ctx *ictx, int num_files, char **files, enum mp_dnd_action action) { input_lock(ictx); - if (!ictx->opts->builtin_dnd) { - TA_FREEP(&ictx->dropped_files); - ictx->dropped_files = talloc_zero_array(ictx, bstr, num_files); - ictx->num_dropped_files = num_files; - ictx->dnd_ts = mp_time_ns(); - ictx->dnd_action = action; - for (int i = 0; i < num_files; i++) - ictx->dropped_files[i] = bstrdup(ictx->dropped_files, bstr0(files[i])); - - notify_event_update(ictx); - input_unlock(ictx); - return; - } - - bool all_sub = true; + TA_FREEP(&ictx->dropped_files); + ictx->dropped_files = talloc_zero_array(ictx, bstr, num_files); + ictx->num_dropped_files = num_files; + ictx->dnd_ts = mp_time_ns(); + ictx->dnd_action = action; for (int i = 0; i < num_files; i++) - all_sub &= mp_might_be_subtitle_file(files[i]); + ictx->dropped_files[i] = bstrdup(ictx->dropped_files, bstr0(files[i])); - if (all_sub) { - for (int i = 0; i < num_files; i++) { - const char *cmd[] = { - "osd-auto", - "sub-add", - files[i], - NULL - }; - queue_cmd(ictx, mp_input_parse_cmd_strv(ictx->log, cmd)); - } - } else if (action == DND_INSERT_NEXT) { - /* To insert the entries in the correct order, we iterate over them - backwards */ - for (int i = num_files - 1; i >= 0; i--) { - const char *cmd[] = { - "osd-auto", - "loadfile", - files[i], - /* Since we're inserting in reverse, wait til the final item - is added to start playing */ - (i > 0) ? "insert-next" : "insert-next-play", - NULL - }; - queue_cmd(ictx, mp_input_parse_cmd_strv(ictx->log, cmd)); - } - } else { - for (int i = 0; i < num_files; i++) { - const char *cmd[] = { - "osd-auto", - "loadfile", - files[i], - /* Either start playing the dropped files right away - or add them to the end of the current playlist */ - (i == 0 && action == DND_REPLACE) ? "replace" : "append-play", - NULL - }; - queue_cmd(ictx, mp_input_parse_cmd_strv(ictx->log, cmd)); - } - } + notify_event_update(ictx); input_unlock(ictx); } diff --git a/meson.build b/meson.build index d7bfd5f3a3..ab09af6117 100644 --- a/meson.build +++ b/meson.build @@ -129,6 +129,7 @@ sources = files( ## Input 'input/cmd.c', + 'input/dnd.c', 'input/event.c', 'input/input.c', 'input/ipc.c', diff --git a/options/options.c b/options/options.c index e23aff9d49..91c471822d 100644 --- a/options/options.c +++ b/options/options.c @@ -920,6 +920,7 @@ static const m_option_t mp_opts[] = { {"", OPT_SUBSTRUCT(resample_opts, resample_conf)}, {"", OPT_SUBSTRUCT(input_opts, input_config)}, + {"input-builtin-drag-and-drop", OPT_BOOL(builtin_dnd)}, {"clipboard", OPT_SUBSTRUCT(clipboard_opts, clipboard_conf)}, @@ -1076,6 +1077,7 @@ static const struct MPOpts mp_default_opts = { .screenshot_template = "mpv-shot%n", .play_dir = 1, .media_controls = true, + .builtin_dnd = true, .video_exts = (char *[]){ "3g2", "3gp", "avi", "flv", "ivf", "m2ts", "m4v", "mj2", "mkv", "mov", "mp4", "mpeg", "mpg", "mxf", "ogv", "rmvb", "ts", "webm", "wmv", "y4m", diff --git a/options/options.h b/options/options.h index 966ac23945..a8950df4a2 100644 --- a/options/options.h +++ b/options/options.h @@ -390,6 +390,7 @@ typedef struct MPOpts { struct hwdec_opts *hwdec_opts; struct input_opts *input_opts; + bool builtin_dnd; struct clipboard_opts *clipboard_opts; diff --git a/player/main.c b/player/main.c index 30302b0476..23e89005e3 100644 --- a/player/main.c +++ b/player/main.c @@ -56,6 +56,7 @@ #include "common/playlist.h" #include "options/options.h" #include "options/path.h" +#include "input/dnd.h" #include "input/input.h" #include "demux/packet_pool.h" @@ -420,6 +421,8 @@ int mp_initialize(struct MPContext *mpctx, char **options) mp_smtc_init(mp_new_client(mpctx->clients, "SystemMediaTransportControls")); #endif + mp_dnd_init(mp_new_client(mpctx->clients, "dnd")); + mpctx->ipc_ctx = mp_init_ipc(mpctx->clients, mpctx->global); if (opts->encode_opts->file && opts->encode_opts->file[0]) {