diff --git a/macos/Sources/Ghostty/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/SurfaceView_AppKit.swift index 03c113209..22784d164 100644 --- a/macos/Sources/Ghostty/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/SurfaceView_AppKit.swift @@ -1815,18 +1815,39 @@ extension Ghostty.SurfaceView: NSServicesMenuRequestor { forSendType sendType: NSPasteboard.PasteboardType?, returnType: NSPasteboard.PasteboardType? ) -> Any? { - // Types that we accept sent to us - let accepted: [NSPasteboard.PasteboardType] = [.string, .init("public.utf8-plain-text")] + // This function confused me a bit so I'm going to add my own commentary on + // how this works. macOS sends this callback with the given send/return types and + // we must return the responder capable of handling the COMBINATION of those send + // and return types (or super up if we can't handle it). + // + // The "COMBINATION" bit is key: we might get sent a string (we can handle that) + // but get requested an image (we can't handle that at the time of writing this), + // so we must bubble up. + + // Types we can receive + let receivable: [NSPasteboard.PasteboardType] = [.string, .init("public.utf8-plain-text")] + + // Types that we can send. Currently the same as receivable but I'm separating + // this out so we can modify this in the future. + let sendable: [NSPasteboard.PasteboardType] = receivable + + // The sendable types that require a selection (currently all) + let sendableRequiresSelection = sendable - // We can always receive the accepted types - if (returnType == nil || accepted.contains(returnType!)) { - return self - } - - // If we have a selection we can send the accepted types too - if ((self.surface != nil && ghostty_surface_has_selection(self.surface)) && - (sendType == nil || accepted.contains(sendType!)) - ) { + // If we expect no data to be sent/received we can obviously handle it (that's + // the nil check), otherwise it must conform to the types we support on both sides. + if (returnType == nil || receivable.contains(returnType!)) && + (sendType == nil || sendable.contains(sendType!)) { + // If we're expected to send back a type that requires selection, then + // verify that we have a selection. We do this within this block because + // validateRequestor is called a LOT and we want to prevent unnecessary + // performance hits because `ghostty_surface_has_selection` isn't free. + if let sendType, sendableRequiresSelection.contains(sendType) { + if surface == nil || !ghostty_surface_has_selection(surface) { + return super.validRequestor(forSendType: sendType, returnType: returnType) + } + } + return self }