#import #include #include // cdropPasteboardChangeCount returns the general pasteboard's monotonic change // counter. Polling it is the cheap way to detect a copy without a callback API. long cdropPasteboardChangeCount(void) { return (long)[[NSPasteboard generalPasteboard] changeCount]; } // cdropPasteboardReadText returns a malloc'd UTF-8 copy of the pasteboard's // plain-text payload (caller frees), or NULL when there is no text (image / // file only). It sets *sensitive to 1 when a privacy marker type is present — // org.nspasteboard.{Concealed,Transient,AutoGenerated}Type — so the upload // policy can skip it. (Most password managers set no marker; this is a // best-effort secondary guard behind the server's short TTL.) // cdropIsStagingPath 判断字符串是否为 Universal Clipboard / Handoff 的暂存文件路径——富文本 // 复制时 pasteboard 的纯文本表示有时是 .../shared-pasteboard/.../xxx.rtfd 这样的路径而非内容, // 绝不能当剪贴板文本上传同步。 static BOOL cdropIsStagingPath(NSString *s) { if (s == nil) { return NO; } return ([s rangeOfString:@"/shared-pasteboard/"].location != NSNotFound) || ([s rangeOfString:@"com.apple.coreservices.useractivityd"].location != NSNotFound); } // cdropPlainFromRich 从 RTF / RTFD 富文本派生纯文本(复制富文本时纯文本类型可能缺失或为暂存 // 路径)。取不到返回 nil。 static NSString *cdropPlainFromRich(NSPasteboard *pb) { NSData *data = [pb dataForType:NSPasteboardTypeRTFD]; NSAttributedString *as = nil; if (data) { as = [[NSAttributedString alloc] initWithData:data options:@{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType} documentAttributes:nil error:nil]; } if (as == nil) { data = [pb dataForType:NSPasteboardTypeRTF]; if (data) { as = [[NSAttributedString alloc] initWithData:data options:@{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType} documentAttributes:nil error:nil]; } } return as ? as.string : nil; } char *cdropPasteboardReadText(int *sensitive) { NSPasteboard *pb = [NSPasteboard generalPasteboard]; *sensitive = 0; for (NSString *type in pb.types) { if ([type isEqualToString:@"org.nspasteboard.ConcealedType"] || [type isEqualToString:@"org.nspasteboard.TransientType"] || [type isEqualToString:@"org.nspasteboard.AutoGeneratedType"]) { *sensitive = 1; break; } } NSString *s = [pb stringForType:NSPasteboardTypeString]; // 纯文本缺失或取到的是 Handoff 暂存路径 → 从 RTF/RTFD 派生真正的文本。 if (s == nil || cdropIsStagingPath(s)) { s = cdropPlainFromRich(pb); } if (s == nil || cdropIsStagingPath(s)) { return NULL; } // 仍拿不到文本:当非文本忽略 const char *utf8 = [s UTF8String]; if (utf8 == NULL) { return NULL; } return strdup(utf8); } // cdropPasteboardWriteText replaces the pasteboard's contents with plain text. void cdropPasteboardWriteText(const char *text) { NSString *s = [NSString stringWithUTF8String:text]; if (s == nil) { return; } NSPasteboard *pb = [NSPasteboard generalPasteboard]; [pb clearContents]; [pb setString:s forType:NSPasteboardTypeString]; }