#pragma once #import #include #include // macOS 剪贴板操作封装 // 使用 NSPasteboard API 实现 class ClipboardHandler { public: // 检查剪贴板功能是否可用 (macOS 总是可用) static bool IsAvailable() { return true; } // 获取剪贴板中的文件列表 // 返回文件的完整路径列表(UTF-8),失败返回空列表 static std::vector GetFiles() { std::vector files; @autoreleasepool { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; // 方法1: 尝试获取文件 URL 列表 (macOS 10.13+) NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]] options:@{NSPasteboardURLReadingFileURLsOnlyKey: @YES}]; if (urls && urls.count > 0) { for (NSURL* url in urls) { if (url.isFileURL) { NSString* path = url.path; if (path) { files.push_back([path UTF8String]); } } } return files; } // 方法2: 兼容旧版 API (NSFilenamesPboardType) NSArray* filenames = [pasteboard propertyListForType:NSFilenamesPboardType]; if (filenames && [filenames isKindOfClass:[NSArray class]]) { for (NSString* path in filenames) { if ([path isKindOfClass:[NSString class]]) { files.push_back([path UTF8String]); } } } } return files; } // 获取剪贴板文本 // 返回 UTF-8 编码的文本,失败返回空字符串 static std::string GetText() { @autoreleasepool { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; NSString* text = [pasteboard stringForType:NSPasteboardTypeString]; if (text) { return [text UTF8String]; } } return ""; } // 设置剪贴板文本 // text: UTF-8 编码的文本 // 返回是否成功 static bool SetText(const std::string& text) { if (text.empty()) return true; @autoreleasepool { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; [pasteboard clearContents]; NSString* nsText = [NSString stringWithUTF8String:text.c_str()]; if (nsText) { return [pasteboard setString:nsText forType:NSPasteboardTypeString]; } } return false; } // 设置剪贴板文本(从原始字节) // data: 文本数据(可能是 GBK 或 UTF-8) // len: 数据长度 static bool SetTextRaw(const char* data, size_t len) { if (!data || len == 0) return true; // 服务端发来的文本可能是 GBK 编码,尝试转换为 UTF-8 std::string text = ConvertToUtf8(data, len); return SetText(text); } // 设置剪贴板文件列表 // files: UTF-8 编码的文件路径列表 // 返回是否成功 static bool SetFiles(const std::vector& files) { if (files.empty()) return true; @autoreleasepool { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; [pasteboard clearContents]; NSMutableArray* urls = [NSMutableArray arrayWithCapacity:files.size()]; for (const auto& path : files) { NSString* nsPath = [NSString stringWithUTF8String:path.c_str()]; if (nsPath) { NSURL* url = [NSURL fileURLWithPath:nsPath]; if (url) { [urls addObject:url]; } } } if (urls.count > 0) { return [pasteboard writeObjects:urls]; } } return false; } private: // 检查是否是有效的 UTF-8 序列 static bool IsValidUtf8(const char* data, size_t len) { const unsigned char* bytes = (const unsigned char*)data; size_t i = 0; while (i < len) { if (bytes[i] <= 0x7F) { // ASCII i++; } else if ((bytes[i] & 0xE0) == 0xC0) { // 2-byte sequence if (i + 1 >= len || (bytes[i + 1] & 0xC0) != 0x80) return false; i += 2; } else if ((bytes[i] & 0xF0) == 0xE0) { // 3-byte sequence if (i + 2 >= len || (bytes[i + 1] & 0xC0) != 0x80 || (bytes[i + 2] & 0xC0) != 0x80) return false; i += 3; } else if ((bytes[i] & 0xF8) == 0xF0) { // 4-byte sequence if (i + 3 >= len || (bytes[i + 1] & 0xC0) != 0x80 || (bytes[i + 2] & 0xC0) != 0x80 || (bytes[i + 3] & 0xC0) != 0x80) return false; i += 4; } else { return false; } } return true; } // 尝试将 GBK 转换为 UTF-8 // 如果已经是 UTF-8,直接返回 static std::string ConvertToUtf8(const char* data, size_t len) { // 检查是否已经是有效的 UTF-8 if (IsValidUtf8(data, len)) { return std::string(data, len); } // 使用 NSString 进行编码转换 (GBK = CFStringEncodingGB_18030_2000) @autoreleasepool { // 尝试 GBK (GB18030) 编码 NSStringEncoding gbkEncoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); NSString* str = [[NSString alloc] initWithBytes:data length:len encoding:gbkEncoding]; if (str) { const char* utf8 = [str UTF8String]; if (utf8) { return std::string(utf8); } } } // 转换失败,返回原始数据 return std::string(data, len); } };