git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

HexFiend/HFPasteboardOwner.m

      1 //
      2 //  HFPasteboardOwner.m
      3 //  HexFiend_2
      4 //
      5 //  Copyright 2008 ridiculous_fish. All rights reserved.
      6 //
      7 
      8 #import <HexFiend/HFPasteboardOwner.h>
      9 #import <HexFiend/HFController.h>
     10 #import <HexFiend/HFByteArray.h>
     11 #import <objc/message.h>
     12 
     13 NSString *const HFPrivateByteArrayPboardType = @"HFPrivateByteArrayPboardType";
     14 
     15 @implementation HFPasteboardOwner
     16 
     17 + (void)initialize {
     18     if (self == [HFPasteboardOwner class]) {
     19         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(prepareCommonPasteboardsForChangeInFileNotification:) name:HFPrepareForChangeInFileNotification object:nil];
     20     }
     21 }
     22 
     23 - (instancetype)initWithPasteboard:(NSPasteboard *)pboard forByteArray:(HFByteArray *)array withTypes:(NSArray *)types {
     24     REQUIRE_NOT_NULL(pboard);
     25     REQUIRE_NOT_NULL(array);
     26     REQUIRE_NOT_NULL(types);
     27     self = [super init];
     28     byteArray = [array retain];
     29     pasteboard = pboard;
     30     [pasteboard declareTypes:types owner:self];
     31     
     32     // get notified when we're about to write a file, so that if they're overwriting a file backing part of our byte array, we can properly clear or preserve our pasteboard
     33     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeInFileNotification:) name:HFPrepareForChangeInFileNotification object:nil];
     34     
     35     return self;
     36 }
     37 + (id)ownPasteboard:(NSPasteboard *)pboard forByteArray:(HFByteArray *)array withTypes:(NSArray *)types {
     38     return [[[self alloc] initWithPasteboard:pboard forByteArray:array withTypes:types] autorelease];
     39 }
     40 
     41 - (void)tearDownPasteboardReferenceIfExists {
     42     if (pasteboard) {
     43         pasteboard = nil;
     44         [[NSNotificationCenter defaultCenter] removeObserver:self name:HFPrepareForChangeInFileNotification object:nil];
     45     }
     46     if (retainedSelfOnBehalfOfPboard) {
     47         [self release];
     48         retainedSelfOnBehalfOfPboard = NO;
     49     }
     50 }
     51 
     52 
     53 + (HFByteArray *)_unpackByteArrayFromDictionary:(NSDictionary *)byteArrayDictionary {
     54     HFByteArray *result = nil;
     55     if (byteArrayDictionary) {
     56         NSString *uuid = byteArrayDictionary[@"HFUUID"];
     57         if ([uuid isEqual:[self uuid]]) {
     58             result = (HFByteArray *)[byteArrayDictionary[@"HFByteArray"] unsignedLongValue];
     59         }
     60     }
     61     return result;
     62 }
     63 
     64 + (HFByteArray *)unpackByteArrayFromPasteboard:(NSPasteboard *)pasteboard {
     65     REQUIRE_NOT_NULL(pasteboard);
     66     HFByteArray *result = [self _unpackByteArrayFromDictionary:[pasteboard propertyListForType:HFPrivateByteArrayPboardType]];
     67     return result;
     68 }
     69 
     70 /* Try to fix up commonly named pasteboards when a file is about to be saved */
     71 + (void)prepareCommonPasteboardsForChangeInFileNotification:(NSNotification *)notification {
     72     const BOOL *cancellationPointer = [[notification userInfo][HFChangeInFileShouldCancelKey] pointerValue];
     73     if (*cancellationPointer) return; //don't do anything if someone requested cancellation
     74     
     75     NSDictionary *userInfo = [notification userInfo];
     76     NSArray *changedRanges = userInfo[HFChangeInFileModifiedRangesKey];
     77     HFFileReference *fileReference = [notification object];
     78     NSMutableDictionary *hint = userInfo[HFChangeInFileHintKey];
     79     
     80     NSString * const names[] = {NSGeneralPboard, NSFindPboard, NSDragPboard};
     81     NSUInteger i;
     82     for (i=0; i < sizeof names / sizeof *names; i++) {
     83         NSPasteboard *pboard = [NSPasteboard pasteboardWithName:names[i]];
     84         HFByteArray *byteArray = [self unpackByteArrayFromPasteboard:pboard];
     85         if (byteArray && ! [byteArray clearDependenciesOnRanges:changedRanges inFile:fileReference hint:hint]) {
     86             /* This pasteboard no longer works */
     87             [pboard declareTypes:@[] owner:nil];
     88         }
     89     }
     90 }
     91 
     92 - (void)changeInFileNotification:(NSNotification *)notification {
     93     HFASSERT(pasteboard != nil);
     94     HFASSERT(byteArray != nil);
     95     NSDictionary *userInfo = [notification userInfo];
     96     const BOOL *cancellationPointer = [userInfo[HFChangeInFileShouldCancelKey] pointerValue];
     97     if (*cancellationPointer) return; //don't do anything if someone requested cancellation
     98     NSMutableDictionary *hint = userInfo[HFChangeInFileHintKey];
     99     
    100     NSArray *changedRanges = [notification userInfo][HFChangeInFileModifiedRangesKey];
    101     HFFileReference *fileReference = [notification object];
    102     if (! [byteArray clearDependenciesOnRanges:changedRanges inFile:fileReference hint:hint]) {
    103         /* We can't do it */
    104         [self tearDownPasteboardReferenceIfExists];
    105     }
    106 }
    107 
    108 - (void)dealloc {
    109     [self tearDownPasteboardReferenceIfExists];
    110     [byteArray release];
    111     [super dealloc];
    112 }
    113 
    114 - (void)writeDataInBackgroundToPasteboard:(NSPasteboard *)pboard ofLength:(unsigned long long)length forType:(NSString *)type trackingProgress:(id)tracker {
    115     USE(length);
    116     USE(pboard);
    117     USE(type);
    118     USE(tracker);
    119     UNIMPLEMENTED_VOID();
    120 }
    121 
    122 - (void)backgroundMoveDataToPasteboard:(NSString *)type {
    123     @autoreleasepool {
    124     [self writeDataInBackgroundToPasteboard:pasteboard ofLength:dataAmountToCopy forType:type trackingProgress:nil];
    125     [self performSelectorOnMainThread:@selector(backgroundMoveDataFinished:) withObject:nil waitUntilDone:NO];
    126     }
    127 }
    128 
    129 - (void)backgroundMoveDataFinished:unused {
    130     USE(unused);
    131     HFASSERT(backgroundCopyOperationFinished == NO);
    132     backgroundCopyOperationFinished = YES;
    133     if (! didStartModalSessionForBackgroundCopyOperation) {
    134         /* We haven't started the modal session, so make sure it never happens */
    135         [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(beginModalSessionForBackgroundCopyOperation:) object:nil];
    136         CFRunLoopWakeUp(CFRunLoopGetCurrent());
    137     }
    138     else {
    139         /* We have started the modal session, so end it. */
    140         [NSApp stopModalWithCode:0];
    141         //stopModal: won't trigger unless we post a do-nothing event
    142         NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:0 context:NULL subtype:0 data1:0 data2:0];
    143         [NSApp postEvent:event atStart:NO];
    144     }
    145 }
    146 
    147 - (void)beginModalSessionForBackgroundCopyOperation:(id)unused {
    148     USE(unused);
    149     HFASSERT(backgroundCopyOperationFinished == NO);
    150     HFASSERT(didStartModalSessionForBackgroundCopyOperation == NO);
    151     didStartModalSessionForBackgroundCopyOperation = YES;
    152 }
    153 
    154 - (BOOL)moveDataWithProgressReportingToPasteboard:(NSPasteboard *)pboard forType:(NSString *)type {
    155     // The -[NSRunLoop runMode:beforeDate:] call in the middle of this function can cause it to be
    156     // called reentrantly, which was previously causing leaks and use-after-free crashes. For
    157     // some reason this happens basically always when copying lots of data into VMware Fusion.
    158     // I'm not even sure what the ideal behavior would be here, but am fairly certain that this
    159     // is the best that can be done without rewriting a portion of the background copying code.
    160     // TODO: Figure out what the ideal behavior should be here.
    161     
    162     HFASSERT(pboard == pasteboard);
    163     [self retain]; //resolving the pasteboard may release us, which deallocates us, which deallocates our tracker...make sure we survive through this function
    164     /* Give the user a chance to request a smaller amount if it's really big */
    165     unsigned long long availableAmount = [byteArray length];
    166     unsigned long long amountToCopy = [self amountToCopyForDataLength:availableAmount stringLength:[self stringLengthForDataLength:availableAmount]];
    167     if (amountToCopy > 0) {
    168 
    169         backgroundCopyOperationFinished = NO;
    170         didStartModalSessionForBackgroundCopyOperation = NO;
    171         dataAmountToCopy = amountToCopy;
    172         [NSThread detachNewThreadSelector:@selector(backgroundMoveDataToPasteboard:) toTarget:self withObject:type];
    173         [self performSelector:@selector(beginModalSessionForBackgroundCopyOperation:) withObject:nil afterDelay:1.0 inModes:@[NSModalPanelRunLoopMode]];
    174         while (! backgroundCopyOperationFinished) {
    175             [[NSRunLoop currentRunLoop] runMode:NSModalPanelRunLoopMode beforeDate:[NSDate distantFuture]];
    176         }
    177     }
    178     [self release];
    179     return YES;
    180 }
    181 
    182 - (void)pasteboardChangedOwner:(NSPasteboard *)pboard {
    183     HFASSERT(pasteboard == pboard);
    184     [self tearDownPasteboardReferenceIfExists];
    185 }
    186 
    187 - (HFByteArray *)byteArray {
    188     return byteArray;
    189 }
    190 
    191 - (void)pasteboard:(NSPasteboard *)pboard provideDataForType:(NSString *)type {
    192     if (! pasteboard) {
    193         /* Don't do anything, because we've torn down our pasteboard */
    194         return;
    195     }
    196     if ([type isEqualToString:HFPrivateByteArrayPboardType]) {
    197         if (! retainedSelfOnBehalfOfPboard) {
    198             retainedSelfOnBehalfOfPboard = YES;
    199             CFRetain(self);
    200         }
    201         NSDictionary *dict = @{@"HFByteArray": @((unsigned long)byteArray),
    202                               @"HFUUID": [[self class] uuid]};
    203         [pboard setPropertyList:dict forType:type];
    204     }
    205     else {
    206         if (! [self moveDataWithProgressReportingToPasteboard:pboard forType:type]) {
    207             [pboard setData:[NSData data] forType:type];
    208         }
    209     }
    210 }
    211 
    212 - (void)setBytesPerLine:(NSUInteger)val { bytesPerLine = val; }
    213 - (NSUInteger)bytesPerLine { return bytesPerLine; }
    214 
    215 + (NSString *)uuid {
    216     static NSString *uuid;
    217     if (! uuid) {
    218         CFUUIDRef uuidRef = CFUUIDCreate(NULL);
    219         uuid = (NSString *)CFUUIDCreateString(NULL, uuidRef);
    220         CFRelease(uuidRef);
    221     }
    222     return uuid;
    223 }
    224 
    225 - (unsigned long long)stringLengthForDataLength:(unsigned long long)dataLength { USE(dataLength); UNIMPLEMENTED(); }
    226 
    227 - (unsigned long long)amountToCopyForDataLength:(unsigned long long)numBytes stringLength:(unsigned long long)stringLength {
    228     unsigned long long dataLengthResult, stringLengthResult;
    229     NSInteger alertReturn = NSIntegerMax;
    230     const unsigned long long copyOption1 = MAXIMUM_PASTEBOARD_SIZE_TO_EXPORT;
    231     const unsigned long long copyOption2 = MINIMUM_PASTEBOARD_SIZE_TO_WARN_ABOUT;
    232     NSString *option1String = HFDescribeByteCount(copyOption1);
    233     NSString *option2String = HFDescribeByteCount(copyOption2);
    234     NSString* dataSizeDescription = HFDescribeByteCount(stringLength);
    235     if (stringLength >= MAXIMUM_PASTEBOARD_SIZE_TO_EXPORT) {
    236         NSString *option1 = [@"Copy " stringByAppendingString:option1String];
    237         NSString *option2 = [@"Copy " stringByAppendingString:option2String];
    238         alertReturn = NSRunAlertPanel(@"Large Clipboard", @"The copied data would occupy %@ if written to the clipboard.  This is larger than the system clipboard supports.  Do you want to copy only part of the data?", @"Cancel",  option1, option2, dataSizeDescription);
    239         switch (alertReturn) {
    240             case NSAlertDefaultReturn:
    241             default:
    242                 stringLengthResult = 0;
    243                 break;
    244             case NSAlertAlternateReturn:
    245                 stringLengthResult = copyOption1;
    246                 break;
    247             case NSAlertOtherReturn:
    248                 stringLengthResult = copyOption2;
    249                 break;
    250         }
    251         
    252     }
    253     else if (stringLength >= MINIMUM_PASTEBOARD_SIZE_TO_WARN_ABOUT) {
    254         NSString *option1 = [@"Copy " stringByAppendingString:HFDescribeByteCount(stringLength)];
    255         NSString *option2 = [@"Copy " stringByAppendingString:HFDescribeByteCount(copyOption2)];
    256         alertReturn = NSRunAlertPanel(@"Large Clipboard", @"The copied data would occupy %@ if written to the clipboard.  Performing this copy may take a long time.  Do you want to copy only part of the data?", @"Cancel",  option1, option2, dataSizeDescription);
    257         switch (alertReturn) {
    258             case NSAlertDefaultReturn:
    259             default:
    260                 stringLengthResult = 0;
    261                 break;
    262             case NSAlertAlternateReturn:
    263                 stringLengthResult = stringLength;
    264                 break;
    265             case NSAlertOtherReturn:
    266                 stringLengthResult = copyOption2;
    267                 break;
    268         }
    269     }
    270     else {
    271         /* Small enough to copy it all */
    272         stringLengthResult = stringLength;
    273     }
    274     
    275     /* Convert from string length to data length */
    276     if (stringLengthResult == stringLength) {
    277         dataLengthResult = numBytes;
    278     }
    279     else {
    280         unsigned long long divisor = stringLength / numBytes;
    281         dataLengthResult = stringLengthResult / divisor;
    282     }
    283     
    284     return dataLengthResult;
    285 }
    286 
    287 @end

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.