From dda09260da4a5cbc0c6333985e9c1888805a7fd6 Mon Sep 17 00:00:00 2001 From: der richter Date: Sun, 5 Jul 2026 11:52:02 +0200 Subject: [PATCH 1/5] input/dnd: expose handle_dnd function expose this function so it can be reused internally without the need of the dnd option handling and as a generic file open function with some special file type handling. similar to the mp_input_drop_files function. --- input/dnd.c | 17 +++++++++++++++++ input/dnd.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/input/dnd.c b/input/dnd.c index b03b2bb4869a2..7075cf97df188 100644 --- a/input/dnd.c +++ b/input/dnd.c @@ -140,6 +140,23 @@ static MP_THREAD_VOID mpv_event_loop_fn(void *arg) MP_THREAD_RETURN(); } +void mp_dnd_load_file(mpv_handle *mpv, int num_files, char **files, enum mp_dnd_action action) +{ + mpv_node *items = talloc_zero_array(NULL, mpv_node, num_files); + mpv_node_list list = {.values = items, .num = num_files}; + mpv_node node = {.format = MPV_FORMAT_NODE_ARRAY, .u = {.list = &list}}; + for (int n = 0; n < num_files; n++) { + items[n] = (mpv_node){.format = MPV_FORMAT_STRING, + .u = {.string = files[n]}}; + } + + char *actionstr = action == DND_REPLACE ? "replace" : + action == DND_APPEND ? "append" : + action == DND_INSERT_NEXT ? "insert-next" : + "none"; + handle_dnd(mpv, &node, actionstr); +} + void mp_dnd_init(mpv_handle *mpv) { mp_thread mpv_event_loop; diff --git a/input/dnd.h b/input/dnd.h index 8bb9f0ed9d7ea..ddb0503772d2e 100644 --- a/input/dnd.h +++ b/input/dnd.h @@ -18,4 +18,7 @@ #include "player/client.h" +#include "event.h" + +void mp_dnd_load_file(mpv_handle *ctx, int num_files, char **files, enum mp_dnd_action action); void mp_dnd_init(mpv_handle *mpv); From ffc9e6a73c7f343e9af2b9fb4f5b6c1159031e6c Mon Sep 17 00:00:00 2001 From: der richter Date: Sun, 5 Jul 2026 11:53:18 +0200 Subject: [PATCH 2/5] mac/input: make open file handling reusable make it reusable so the generic file logic can be reused, but handled by different underlying similar function, like dnd or just open file. --- osdep/mac/input_helper.swift | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/osdep/mac/input_helper.swift b/osdep/mac/input_helper.swift index 3833dc9fc5db2..8237084e4f6dc 100644 --- a/osdep/mac/input_helper.swift +++ b/osdep/mac/input_helper.swift @@ -253,16 +253,23 @@ class InputHelper: NSObject { lock.withLock { guard let input = input else { return } - var action = DND_APPEND - if !append { - action = NSEvent.modifierFlags.contains(.shift) ? DND_APPEND : DND_REPLACE + open(files: files, append: append) { (filesPtr, action) in + mp_input_drop_files(input, Int32(files.count), &filesPtr, action) } + } + } - let filesClean = files.map { $0.hasPrefix("file:///.file/id=") ? (URL(string: $0)?.path ?? $0) : $0 } - var filesPtr = filesClean.map { UnsafeMutablePointer(strdup($0)) } - mp_input_drop_files(input, Int32(files.count), &filesPtr, action) - for charPtr in filesPtr { free(UnsafeMutablePointer(mutating: charPtr)) } + func open(files: [String], append: Bool = false, + completion: (inout [UnsafeMutablePointer?], mp_dnd_action) -> Void) { + var action = DND_APPEND + if !append { + action = NSEvent.modifierFlags.contains(.shift) ? DND_APPEND : DND_REPLACE } + + let filesClean = files.map { $0.hasPrefix("file:///.file/id=") ? (URL(string: $0)?.path ?? $0) : $0 } + var filesPtr = filesClean.map { UnsafeMutablePointer(strdup($0)) } + completion(&filesPtr, action) + for charPtr in filesPtr { free(UnsafeMutablePointer(mutating: charPtr)) } } private func useAltGr() -> Bool { From 711be5a7e34aaca6814ef765d3a0a179823aa59f Mon Sep 17 00:00:00 2001 From: der richter Date: Sun, 5 Jul 2026 11:58:26 +0200 Subject: [PATCH 3/5] mac/apphub: add new open file function, use it for system file events the old dnd open file mechanism was reworked with 81611b4a4e267999618fa762eacd8614f36cea6b and isn't ready to handle open file events as early as needed anymore. additionally it also handles dnd options that would prevent loading of files in the case of not dnded files, which doesn't make much sense. sadly the macOS system file event exposes two different cases, open from finder and dnd on the dock icon. this can't be distinguished in any way. hence it was decided that dnd on the dock icon will be handled like a generic file open event now and only dnd on the window will be a proper dnd action. use the newly added open file function to circumvent the dnd option handling in those cases. Fixes #17921 --- osdep/mac/app_bridge_objc.h | 1 + osdep/mac/app_hub.swift | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/osdep/mac/app_bridge_objc.h b/osdep/mac/app_bridge_objc.h index bcde2caf8a6cd..b317e2f9cd94e 100644 --- a/osdep/mac/app_bridge_objc.h +++ b/osdep/mac/app_bridge_objc.h @@ -28,6 +28,7 @@ #include "common/global.h" #include "input/input.h" #include "input/event.h" +#include "input/dnd.h" #include "input/keycodes.h" #include "video/out/win_state.h" diff --git a/osdep/mac/app_hub.swift b/osdep/mac/app_hub.swift index 879b084ca5aad..b050bb124abe4 100644 --- a/osdep/mac/app_hub.swift +++ b/osdep/mac/app_hub.swift @@ -39,6 +39,7 @@ class AppHub: NSObject { var isApplication: Bool { return NSApp is Application } var isBundle: Bool { return ProcessInfo.processInfo.environment["MPVBUNDLE"] == "true" } var openEvents: Int = 0 + var openFiles: [String] = [] private override init() { input = InputHelper() @@ -113,9 +114,22 @@ class AppHub: NSObject { return strL.localizedStandardCompare(strR) == .orderedAscending } log.verbose("\(openEvents > 0 ? "Appending" : "Opening") dropped files: \(files)") - input.open(files: files, append: openEvents > 0) - openEvents += 1 + DispatchQueue.main.async { + self.open(files: files, append: self.openEvents > 0) + self.openEvents += 1 + } DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.openEvents -= 1 } + + } + + func open(files: [String], append: Bool = false) { + openFiles += files + guard let mpv = mpv else { return } + + input.open(files: files, append: append) { (filesPtr, action) in + mp_dnd_load_file(mpv, Int32(openFiles.count), &filesPtr, action) + } + openFiles = [] } func getIcon() -> NSImage { From 4a0ba6ed01f45027dc82d4d81b353c7fb1d38682 Mon Sep 17 00:00:00 2001 From: der richter Date: Sun, 5 Jul 2026 12:04:18 +0200 Subject: [PATCH 4/5] mac/menu: use new file open function with out dnd handling the open file menu items aren't dnd events and should not be treated as such. --- osdep/mac/menu_bar.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osdep/mac/menu_bar.swift b/osdep/mac/menu_bar.swift index 2a830ff17b32f..e3b34a8029e24 100644 --- a/osdep/mac/menu_bar.swift +++ b/osdep/mac/menu_bar.swift @@ -334,7 +334,7 @@ class MenuBar: NSObject, EventSubscriber { @objc func openFiles() { guard let files = dialog.openFiles(path: currentDir) else { return } - appHub.input.open(files: files) + appHub.open(files: files) } @objc func openPlaylist() { @@ -344,7 +344,7 @@ class MenuBar: NSObject, EventSubscriber { @objc func openUrl() { guard let file = dialog.openUrl() else { return } - appHub.input.open(files: [file]) + appHub.open(files: [file]) } @objc func command(_ menuItem: MenuItem) { From 0fede24f52e62ca1cfb4453c678bd70b29582699 Mon Sep 17 00:00:00 2001 From: der richter Date: Sun, 5 Jul 2026 12:07:56 +0200 Subject: [PATCH 5/5] mac/input: rename old open file function to handleDnd rename this function to represent better what it does. dnd events should be handled by this one. generic open file events should be handled by the newly introduced function in the apphub, with the same old name. --- osdep/mac/input_helper.swift | 4 ++-- video/out/mac/view.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osdep/mac/input_helper.swift b/osdep/mac/input_helper.swift index 8237084e4f6dc..c84d71971471f 100644 --- a/osdep/mac/input_helper.swift +++ b/osdep/mac/input_helper.swift @@ -249,11 +249,11 @@ class InputHelper: NSObject { return String(utf16CodeUnits: chars, count: length) } - @objc func open(files: [String], append: Bool = false) { + @objc func handleDnd(files: [String]) { lock.withLock { guard let input = input else { return } - open(files: files, append: append) { (filesPtr, action) in + open(files: files) { (filesPtr, action) in mp_input_drop_files(input, Int32(files.count), &filesPtr, action) } } diff --git a/video/out/mac/view.swift b/video/out/mac/view.swift index c341b3154a5af..f8cc6254ae975 100644 --- a/video/out/mac/view.swift +++ b/video/out/mac/view.swift @@ -92,7 +92,7 @@ class View: NSView, CALayerDelegate { } } if files.isEmpty { return false } - input?.open(files: files) + input?.handleDnd(files: files) return true }