| /* |
| * MACDRV Cocoa clipboard code |
| * |
| * Copyright 2012, 2013 Ken Thomases for CodeWeavers Inc. |
| * |
| * This library 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. |
| * |
| * This library 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 this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "macdrv_cocoa.h" |
| #import "cocoa_app.h" |
| #import "cocoa_event.h" |
| #import "cocoa_window.h" |
| |
| |
| static int owned_change_count = -1; |
| static int change_count = -1; |
| |
| static NSArray* BitmapOutputTypes; |
| static NSDictionary* BitmapOutputTypeMap; |
| static dispatch_once_t BitmapOutputTypesInitOnce; |
| |
| static NSString* const OwnershipSentinel = @"org.winehq.wine.winemac.pasteboard-ownership-sentinel"; |
| |
| |
| /*********************************************************************** |
| * macdrv_is_pasteboard_owner |
| */ |
| int macdrv_is_pasteboard_owner(macdrv_window w) |
| { |
| __block int ret; |
| WineWindow* window = (WineWindow*)w; |
| |
| OnMainThread(^{ |
| NSPasteboard* pb = [NSPasteboard generalPasteboard]; |
| ret = ([pb changeCount] == owned_change_count); |
| |
| [window.queue discardEventsMatchingMask:event_mask_for_type(LOST_PASTEBOARD_OWNERSHIP) |
| forWindow:window]; |
| }); |
| |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * macdrv_has_pasteboard_changed |
| */ |
| int macdrv_has_pasteboard_changed(void) |
| { |
| __block int new_change_count; |
| int ret; |
| |
| OnMainThread(^{ |
| NSPasteboard* pb = [NSPasteboard generalPasteboard]; |
| new_change_count = [pb changeCount]; |
| }); |
| |
| ret = (change_count != new_change_count); |
| change_count = new_change_count; |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * macdrv_copy_pasteboard_types |
| * |
| * Returns an array of UTI strings for the types of data available on |
| * the pasteboard, or NULL on error. The caller is responsible for |
| * releasing the returned array with CFRelease(). |
| */ |
| CFArrayRef macdrv_copy_pasteboard_types(CFTypeRef pasteboard) |
| { |
| NSPasteboard* pb = (NSPasteboard*)pasteboard; |
| __block CFArrayRef ret = NULL; |
| NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
| |
| dispatch_once(&BitmapOutputTypesInitOnce, ^{ |
| NSArray* bitmapFileTypes = [NSArray arrayWithObjects: |
| [NSNumber numberWithUnsignedInteger:NSTIFFFileType], |
| [NSNumber numberWithUnsignedInteger:NSPNGFileType], |
| [NSNumber numberWithUnsignedInteger:NSBMPFileType], |
| [NSNumber numberWithUnsignedInteger:NSGIFFileType], |
| [NSNumber numberWithUnsignedInteger:NSJPEGFileType], |
| nil]; |
| |
| BitmapOutputTypes = [[NSArray alloc] initWithObjects:@"public.tiff", @"public.png", |
| @"com.microsoft.bmp", @"com.compuserve.gif", @"public.jpeg", nil]; |
| |
| BitmapOutputTypeMap = [[NSDictionary alloc] initWithObjects:bitmapFileTypes |
| forKeys:BitmapOutputTypes]; |
| }); |
| |
| OnMainThread(^{ |
| @try |
| { |
| NSPasteboard* local_pb = pb; |
| NSArray* types; |
| |
| if (!local_pb) local_pb = [NSPasteboard generalPasteboard]; |
| types = [local_pb types]; |
| |
| // If there are any types understood by NSBitmapImageRep, then we |
| // can offer all of the types that it can output, too. For example, |
| // if TIFF is on the pasteboard, we can offer PNG, BMP, etc. to the |
| // Windows program. We'll convert on demand. |
| if ([types firstObjectCommonWithArray:[NSBitmapImageRep imageTypes]] || |
| [types firstObjectCommonWithArray:[NSBitmapImageRep imagePasteboardTypes]]) |
| { |
| NSMutableArray* newTypes = [BitmapOutputTypes mutableCopy]; |
| [newTypes removeObjectsInArray:types]; |
| types = [types arrayByAddingObjectsFromArray:newTypes]; |
| [newTypes release]; |
| } |
| |
| ret = (CFArrayRef)[types copy]; |
| } |
| @catch (id e) |
| { |
| ERR(@"Exception discarded while copying pasteboard types: %@\n", e); |
| } |
| }); |
| |
| [pool release]; |
| return ret; |
| } |
| |
| |
| /*********************************************************************** |
| * macdrv_copy_pasteboard_data |
| * |
| * Returns the pasteboard data for a specified type, or NULL on error or |
| * if there's no such type on the pasteboard. The caller is responsible |
| * for releasing the returned data object with CFRelease(). |
| */ |
| CFDataRef macdrv_copy_pasteboard_data(CFTypeRef pasteboard, CFStringRef type) |
| { |
| NSPasteboard* pb = (NSPasteboard*)pasteboard; |
| __block NSData* ret = nil; |
| |
| OnMainThread(^{ |
| @try |
| { |
| NSPasteboard* local_pb = pb; |
| if (!local_pb) local_pb = [NSPasteboard generalPasteboard]; |
| if ([local_pb availableTypeFromArray:[NSArray arrayWithObject:(NSString*)type]]) |
| ret = [[local_pb dataForType:(NSString*)type] copy]; |
| else |
| { |
| NSNumber* bitmapType = [BitmapOutputTypeMap objectForKey:(NSString*)type]; |
| if (bitmapType) |
| { |
| NSArray* reps = [NSBitmapImageRep imageRepsWithPasteboard:local_pb]; |
| ret = [NSBitmapImageRep representationOfImageRepsInArray:reps |
| usingType:[bitmapType unsignedIntegerValue] |
| properties:nil]; |
| ret = [ret copy]; |
| } |
| } |
| } |
| @catch (id e) |
| { |
| ERR(@"Exception discarded while copying pasteboard types: %@\n", e); |
| } |
| }); |
| |
| return (CFDataRef)ret; |
| } |
| |
| |
| /*********************************************************************** |
| * macdrv_clear_pasteboard |
| * |
| * Takes ownership of the Mac pasteboard and clears it of all data types. |
| */ |
| void macdrv_clear_pasteboard(macdrv_window w) |
| { |
| WineWindow* window = (WineWindow*)w; |
| |
| OnMainThread(^{ |
| @try |
| { |
| NSPasteboard* pb = [NSPasteboard generalPasteboard]; |
| owned_change_count = [pb declareTypes:[NSArray arrayWithObject:OwnershipSentinel] |
| owner:window]; |
| [window.queue discardEventsMatchingMask:event_mask_for_type(LOST_PASTEBOARD_OWNERSHIP) |
| forWindow:window]; |
| } |
| @catch (id e) |
| { |
| ERR(@"Exception discarded while clearing pasteboard: %@\n", e); |
| } |
| }); |
| } |
| |
| |
| /*********************************************************************** |
| * macdrv_set_pasteboard_data |
| * |
| * Sets the pasteboard data for a specified type. Replaces any data of |
| * that type already on the pasteboard. If data is NULL, promises the |
| * type. |
| * |
| * Returns 0 on error, non-zero on success. |
| */ |
| int macdrv_set_pasteboard_data(CFStringRef type, CFDataRef data, macdrv_window w) |
| { |
| __block int ret = 0; |
| WineWindow* window = (WineWindow*)w; |
| |
| OnMainThread(^{ |
| @try |
| { |
| NSPasteboard* pb = [NSPasteboard generalPasteboard]; |
| NSInteger change_count = [pb addTypes:[NSArray arrayWithObject:(NSString*)type] |
| owner:window]; |
| if (change_count) |
| { |
| owned_change_count = change_count; |
| if (data) |
| ret = [pb setData:(NSData*)data forType:(NSString*)type]; |
| else |
| ret = 1; |
| } |
| } |
| @catch (id e) |
| { |
| ERR(@"Exception discarded while copying pasteboard types: %@\n", e); |
| } |
| }); |
| |
| return ret; |
| } |