cocoa_clipboard.m 8.31 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * 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"
23
#import "cocoa_event.h"
24
#import "cocoa_window.h"
25 26


27
static int owned_change_count = -1;
28
static int change_count = -1;
29

30 31 32 33
static NSArray* BitmapOutputTypes;
static NSDictionary* BitmapOutputTypeMap;
static dispatch_once_t BitmapOutputTypesInitOnce;

34 35
static NSString* const OwnershipSentinel = @"org.winehq.wine.winemac.pasteboard-ownership-sentinel";

36 37 38 39

/***********************************************************************
 *              macdrv_is_pasteboard_owner
 */
40
int macdrv_is_pasteboard_owner(macdrv_window w)
41 42
{
    __block int ret;
43
    WineWindow* window = (WineWindow*)w;
44 45 46 47

    OnMainThread(^{
        NSPasteboard* pb = [NSPasteboard generalPasteboard];
        ret = ([pb changeCount] == owned_change_count);
48 49 50

        [window.queue discardEventsMatchingMask:event_mask_for_type(LOST_PASTEBOARD_OWNERSHIP)
                                      forWindow:window];
51 52 53 54 55
    });

    return ret;
}

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
/***********************************************************************
 *              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;
}
73

74 75 76 77 78 79 80
/***********************************************************************
 *              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().
 */
81
CFArrayRef macdrv_copy_pasteboard_types(CFTypeRef pasteboard)
82
{
83
    NSPasteboard* pb = (NSPasteboard*)pasteboard;
84 85 86
    __block CFArrayRef ret = NULL;
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

87 88 89 90 91 92 93 94 95 96
    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",
97
                             @"com.microsoft.bmp", @"com.compuserve.gif", @"public.jpeg", nil];
98 99 100 101 102

        BitmapOutputTypeMap = [[NSDictionary alloc] initWithObjects:bitmapFileTypes
                                                            forKeys:BitmapOutputTypes];
    });

103 104 105
    OnMainThread(^{
        @try
        {
106 107 108 109 110
            NSPasteboard* local_pb = pb;
            NSArray* types;

            if (!local_pb) local_pb = [NSPasteboard generalPasteboard];
            types = [local_pb types];
111 112 113 114 115 116 117 118 119 120 121 122 123 124

            // 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];
            }

125 126 127 128 129 130 131 132 133 134 135
            ret = (CFArrayRef)[types copy];
        }
        @catch (id e)
        {
            ERR(@"Exception discarded while copying pasteboard types: %@\n", e);
        }
    });

    [pool release];
    return ret;
}
136 137 138 139 140 141 142 143 144


/***********************************************************************
 *              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().
 */
145
CFDataRef macdrv_copy_pasteboard_data(CFTypeRef pasteboard, CFStringRef type)
146
{
147
    NSPasteboard* pb = (NSPasteboard*)pasteboard;
148 149 150 151 152
    __block NSData* ret = nil;

    OnMainThread(^{
        @try
        {
153 154 155 156
            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];
157 158 159 160 161
            else
            {
                NSNumber* bitmapType = [BitmapOutputTypeMap objectForKey:(NSString*)type];
                if (bitmapType)
                {
162
                    NSArray* reps = [NSBitmapImageRep imageRepsWithPasteboard:local_pb];
163 164 165 166 167 168
                    ret = [NSBitmapImageRep representationOfImageRepsInArray:reps
                                                                   usingType:[bitmapType unsignedIntegerValue]
                                                                  properties:nil];
                    ret = [ret copy];
                }
            }
169 170 171 172 173 174 175 176 177
        }
        @catch (id e)
        {
            ERR(@"Exception discarded while copying pasteboard types: %@\n", e);
        }
    });

    return (CFDataRef)ret;
}
178 179 180 181 182 183 184


/***********************************************************************
 *              macdrv_clear_pasteboard
 *
 * Takes ownership of the Mac pasteboard and clears it of all data types.
 */
185
void macdrv_clear_pasteboard(macdrv_window w)
186
{
187 188 189
    WineWindow* window = (WineWindow*)w;

    OnMainThread(^{
190 191 192
        @try
        {
            NSPasteboard* pb = [NSPasteboard generalPasteboard];
193 194 195 196
            owned_change_count = [pb declareTypes:[NSArray arrayWithObject:OwnershipSentinel]
                                            owner:window];
            [window.queue discardEventsMatchingMask:event_mask_for_type(LOST_PASTEBOARD_OWNERSHIP)
                                          forWindow:window];
197 198 199 200 201 202 203 204 205 206 207 208 209
        }
        @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
210 211
 * that type already on the pasteboard.  If data is NULL, promises the
 * type.
212 213 214
 *
 * Returns 0 on error, non-zero on success.
 */
215
int macdrv_set_pasteboard_data(CFStringRef type, CFDataRef data, macdrv_window w)
216 217
{
    __block int ret = 0;
218
    WineWindow* window = (WineWindow*)w;
219 220 221 222 223 224

    OnMainThread(^{
        @try
        {
            NSPasteboard* pb = [NSPasteboard generalPasteboard];
            NSInteger change_count = [pb addTypes:[NSArray arrayWithObject:(NSString*)type]
225
                                            owner:window];
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
            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;
}