diff --git a/BasiliskII/src/MacOSX/autorelease.h b/BasiliskII/src/MacOSX/autorelease.h new file mode 100644 index 00000000..49c34e62 --- /dev/null +++ b/BasiliskII/src/MacOSX/autorelease.h @@ -0,0 +1,34 @@ +/* + * autorelease.h - a macro wrapping autorelease pools for use with Objective-C++ source files. + * + * Expands to @autoreleasepool on clang, uses a little hack to emulate @autoreleasepool on gcc. + * + * (C) 2012 Charles Srstka + * + * Feel free to put this under whatever license you wish. + */ + +#import + +#ifndef __Autorelease_H__ +#define __Autorelease_H__ + +// just a little forward compatibility in case we ever support LLVM/clang +#if __clang__ +#define AUTORELEASE_POOL @autoreleasepool +#else +class Autorelease_Pool_Wrapper { +public: + Autorelease_Pool_Wrapper() { m_pool = [[NSAutoreleasePool alloc] init]; } + ~Autorelease_Pool_Wrapper() { [m_pool drain]; } + operator bool() const { return true; } +private: + NSAutoreleasePool *m_pool; +}; + +#define POOL_NAME(x, y) x##_##y +#define POOL_NAME2(x, y) POOL_NAME(x, y) +#define AUTORELEASE_POOL if(Autorelease_Pool_Wrapper POOL_NAME2(pool, __LINE__) = Autorelease_Pool_Wrapper()) +#endif // !__clang__ + +#endif // __Autorelease_H__ diff --git a/BasiliskII/src/MacOSX/clip_macosx64.mm b/BasiliskII/src/MacOSX/clip_macosx64.mm index 84ba0673..6ea5511f 100644 --- a/BasiliskII/src/MacOSX/clip_macosx64.mm +++ b/BasiliskII/src/MacOSX/clip_macosx64.mm @@ -29,6 +29,7 @@ #include "main.h" #include "cpu_emulation.h" #include "emul_op.h" +#include "autorelease.h" #define DEBUG 0 #include "debug.h" @@ -41,16 +42,22 @@ #define TYPE_TEXT FOURCC('T','E','X','T') #define TYPE_STYL FOURCC('s','t','y','l') -static PasteboardRef g_pbref; +static NSPasteboard *g_pboard; // Flag for PutScrap(): the data was put by GetScrap(), don't bounce it back to the MacOS X side static bool we_put_this_data = false; static bool should_clear = false; -static CFStringRef const UTF16_TEXT_FLAVOR_NAME = CFSTR("public.utf16-plain-text"); -static CFStringRef const TEXT_FLAVOR_NAME = CFSTR("com.apple.traditional-mac-plain-text"); -static CFStringRef const STYL_FLAVOR_NAME = CFSTR("net.cebix.basilisk.styl-data"); +static NSMutableDictionary *macScrap; + +// flavor UTIs + +static NSString * const UTF16_TEXT_FLAVOR_NAME = @"public.utf16-plain-text"; +static NSString * const TEXT_FLAVOR_NAME = @"com.apple.traditional-mac-plain-text"; +static NSString * const STYL_FLAVOR_NAME = @"net.cebix.basilisk.styl-data"; + +// font face types enum { FONT_FACE_PLAIN = 0, @@ -63,20 +70,26 @@ enum { FONT_FACE_EXTENDED = 64 }; -static CFStringRef GetUTIFromFlavor(uint32 type) +// Script Manager constants + +#define smRoman 0 +#define smMacSysScript 18 +#define smMacRegionCode 40 + +static NSString *GetUTIFromFlavor(uint32_t type) { switch (type) { - case TYPE_PICT: return kUTTypePICT; + case TYPE_PICT: return (NSString *)kUTTypePICT; case TYPE_TEXT: return TEXT_FLAVOR_NAME; - //case TYPE_TEXT: return UTF16_TEXT_FLAVOR_NAME; + //case TYPE_TEXT: return UTF16_TEXT_FLAVOR_NAME; case TYPE_STYL: return STYL_FLAVOR_NAME; - case FOURCC('m','o','o','v'): return kUTTypeQuickTimeMovie; - case FOURCC('s','n','d',' '): return kUTTypeAudio; - //case FOURCC('u','t','x','t'): return UTF16_TEXT_FLAVOR_NAME; + case FOURCC('m','o','o','v'): return (NSString *)kUTTypeQuickTimeMovie; + case FOURCC('s','n','d',' '): return (NSString *)kUTTypeAudio; + //case FOURCC('u','t','x','t'): return UTF16_TEXT_FLAVOR_NAME; case FOURCC('u','t','1','6'): return UTF16_TEXT_FLAVOR_NAME; - //case FOURCC('u','s','t','l'): return CFSTR("????"); - case FOURCC('i','c','n','s'): return kUTTypeAppleICNS; - default: return NULL; + //case FOURCC('u','s','t','l'): return @"????"; + case FOURCC('i','c','n','s'): return (NSString *)kUTTypeAppleICNS; + default: return nil; } } @@ -84,16 +97,13 @@ static CFStringRef GetUTIFromFlavor(uint32 type) * Get current system script encoding on Mac */ -#define smMacSysScript 18 -#define smMacRegionCode 40 - -static int GetMacScriptManagerVariable(uint16 id) +static int GetMacScriptManagerVariable(uint16_t varID) { int ret = -1; M68kRegisters r; - static uint8 proc[] = { + static uint8_t proc[] = { 0x59, 0x4f, // subq.w #4,sp - 0x3f, 0x3c, 0x00, 0x00, // move.w #id,-(sp) + 0x3f, 0x3c, 0x00, 0x00, // move.w #varID,-(sp) 0x2f, 0x3c, 0x84, 0x02, 0x00, 0x08, // move.l #-2080243704,-(sp) 0xa8, 0xb5, // ScriptUtil() 0x20, 0x1f, // move.l (a7)+,d0 @@ -101,10 +111,36 @@ static int GetMacScriptManagerVariable(uint16 id) }; r.d[0] = sizeof(proc); Execute68kTrap(0xa71e, &r); // NewPtrSysClear() - uint32 proc_area = r.a[0]; + uint32_t proc_area = r.a[0]; if (proc_area) { Host2Mac_memcpy(proc_area, proc, sizeof(proc)); - WriteMacInt16(proc_area + 4, id); + WriteMacInt16(proc_area + 4, varID); + Execute68k(proc_area, &r); + ret = r.d[0]; + r.a[0] = proc_area; + Execute68kTrap(0xa01f, &r); // DisposePtr + } + return ret; +} + +static ScriptCode ScriptNumberForFontID(int16_t fontID) +{ + ScriptCode ret = -1; + M68kRegisters r; + static uint8_t proc[] = { + 0x55, 0x4f, // subq.w #2,sp + 0x3f, 0x3c, 0x00, 0x00, // move.w #fontID,-(sp) + 0x2f, 0x3c, 0x82, 0x02, 0x00, 0x06, // move.l #-2113798138,-(sp) + 0xa8, 0xb5, // ScriptUtil() + 0x30, 0x1f, // move.w (sp)+,d0 + M68K_RTS >> 8, M68K_RTS & 0xff + }; + r.d[0] = sizeof(proc); + Execute68kTrap(0xa71e, &r); // NewPtrSysClear() + uint32_t proc_area = r.a[0]; + if (proc_area) { + Host2Mac_memcpy(proc_area, proc, sizeof(proc)); + WriteMacInt16(proc_area + 4, fontID); Execute68k(proc_area, &r); ret = r.d[0]; r.a[0] = proc_area; @@ -114,63 +150,264 @@ static int GetMacScriptManagerVariable(uint16 id) } /* - * Convert utf-16 from/to system script encoding on Mac + * Get Mac's default text encoding */ -static CFDataRef ConvertMacTextEncoding(CFDataRef pbData, int from_host) +static TextEncoding MacDefaultTextEncoding() { - static TextEncoding g_textEncodingHint = kTextEncodingUnknown; - static UnicodeMapping uMapping; - static UnicodeToTextInfo utInfo; - static TextToUnicodeInfo tuInfo; - static int ready; + int script = GetMacScriptManagerVariable(smMacSysScript); + int region = GetMacScriptManagerVariable(smMacRegionCode); + TextEncoding encoding; - // should we check this only once ? - if (g_textEncodingHint == kTextEncodingUnknown) { - int script = GetMacScriptManagerVariable(smMacSysScript); - int region = GetMacScriptManagerVariable(smMacRegionCode); - if (UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, NULL, &g_textEncodingHint)) - g_textEncodingHint = kTextEncodingMacRoman; - uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0, kTextEncodingDefaultVariant, kUnicode16BitFormat); - uMapping.otherEncoding = GetTextEncodingBase(g_textEncodingHint); - uMapping.mappingVersion = kUnicodeUseLatestMapping; - ready = !CreateUnicodeToTextInfo(&uMapping, &utInfo) && !CreateTextToUnicodeInfo(&uMapping, &tuInfo); - } - if (!ready) - return pbData; + if (UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, NULL, &encoding)) + encoding = kTextEncodingMacRoman; - ByteCount byteCount = CFDataGetLength(pbData); - ByteCount outBytesLength = byteCount * 2; - LogicalAddress outBytesPtr = malloc(byteCount * 2); - if (!outBytesPtr) - return pbData; + return encoding; +} - ByteCount outBytesConverted; - OSStatus err; - if (from_host) { - err = ConvertFromUnicodeToText(utInfo, byteCount, (UniChar *)CFDataGetBytePtr(pbData), - kUnicodeLooseMappingsMask, - 0, NULL, 0, NULL, - outBytesLength, - &outBytesConverted, &outBytesLength, outBytesPtr); - } else { - err = ConvertFromTextToUnicode(tuInfo, byteCount, CFDataGetBytePtr(pbData), - kUnicodeLooseMappingsMask, - 0, NULL, 0, NULL, - outBytesLength, - &outBytesConverted, &outBytesLength, (UniChar *)outBytesPtr); +static NSData *ConvertToMacTextEncoding(NSAttributedString *aStr, NSArray **styleAndScriptRuns) +{ + NSUInteger length = [aStr length]; + + NSMutableArray *styleRuns = [NSMutableArray array]; + + for (NSUInteger index = 0; index < length;) { + NSRange attrRange; + NSDictionary *attrs = [aStr attributesAtIndex:index effectiveRange:&attrRange]; + + [styleRuns addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedInteger:index], @"offset", + attrs, @"attributes", nil]]; + + index = NSMaxRange(attrRange); } - if (err == noErr && outBytesConverted == byteCount) { - CFDataRef pbDataConverted = CFDataCreate(kCFAllocatorDefault, (UInt8*)outBytesPtr, outBytesLength); - if (pbDataConverted) { - CFRelease(pbData); - pbData = pbDataConverted; + UnicodeToTextRunInfo info; + + OSStatus err = CreateUnicodeToTextRunInfoByScriptCode(0, NULL, &info); + + if (err != noErr) { + if (styleAndScriptRuns) + *styleAndScriptRuns = styleRuns; + + return [[aStr string] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(MacDefaultTextEncoding())]; + } + + unichar chars[length]; + + [[aStr string] getCharacters:chars range:NSMakeRange(0, length)]; + + NSUInteger unicodeLength = length * sizeof(unichar); + NSUInteger bufLen = unicodeLength * 2; + uint8_t buf[bufLen]; + ByteCount bytesRead; + + ItemCount scriptRunCount = 1601; // max number of allowed style changes + ScriptCodeRun scriptRuns[scriptRunCount]; + + ItemCount inOffsetCount = [styleRuns count]; + ByteOffset inOffsets[inOffsetCount]; + + if (inOffsetCount) { + for (NSUInteger i = 0; i < inOffsetCount; i++) { + NSDictionary *eachRun = [styleRuns objectAtIndex:i]; + + inOffsets[i] = [[eachRun objectForKey:@"offset"] unsignedLongValue] * 2; } } - free(outBytesPtr); - return pbData; + ItemCount offsetCount; + ByteOffset offsets[inOffsetCount]; + + err = ConvertFromUnicodeToScriptCodeRun(info, unicodeLength, chars, + kUnicodeTextRunMask | kUnicodeUseFallbacksMask | kUnicodeLooseMappingsMask, + inOffsetCount, inOffsets, &offsetCount, offsets, + bufLen, &bytesRead, &bufLen, buf, + scriptRunCount, &scriptRunCount, scriptRuns); + + if (err != noErr) { + if (styleAndScriptRuns) + *styleAndScriptRuns = styleRuns; + + return [[aStr string] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(MacDefaultTextEncoding())]; + } + + if (styleAndScriptRuns) { + NSMutableArray *runs = [NSMutableArray array]; + NSUInteger currentStyleRun = 0; + NSUInteger currentScriptRun = 0; + + for (NSUInteger currentOffset = 0; currentOffset < bufLen;) { + ScriptCodeRun scriptRun = scriptRuns[currentScriptRun]; + NSDictionary *attrs = [[styleRuns objectAtIndex:currentStyleRun] objectForKey:@"attributes"]; + + NSUInteger nextStyleOffset = (currentStyleRun < offsetCount - 1) ? offsets[currentStyleRun + 1] : bufLen; + NSUInteger nextScriptOffset = (currentScriptRun < scriptRunCount - 1) ? scriptRuns[currentScriptRun + 1].offset : bufLen; + + [runs addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedInteger:currentOffset], @"offset", + [NSNumber numberWithShort:scriptRun.script], @"script", + attrs, @"attributes", nil]]; + + if (nextStyleOffset == nextScriptOffset) { + currentStyleRun++; + currentScriptRun++; + currentOffset = nextStyleOffset; + } else if (nextStyleOffset < nextScriptOffset) { + currentStyleRun++; + currentOffset = nextStyleOffset; + } else { + currentScriptRun++; + currentOffset = nextScriptOffset; + } + } + + *styleAndScriptRuns = runs; + } + + return [NSData dataWithBytes:buf length:bufLen]; +} + +/* + * Count all Mac font IDs on the system + */ + +static NSUInteger CountMacFonts() +{ + M68kRegisters r; + static uint8_t proc[] = { + 0x55, 0x4f, // subq.w #2,sp + 0x2f, 0x3c, 'F', 'O', 'N', 'D', // move.l #'FOND',-(sp) + 0xa9, 0x9c, // CountResources() + 0x30, 0x1f, // move.w (sp)+,D0 + M68K_RTS >> 8, M68K_RTS & 0xff + }; + r.d[0] = sizeof(proc); + Execute68kTrap(0xa71e, &r); // NewPtrSysClear() + uint32_t proc_area = r.a[0]; + int16_t fontCount = 0; + + if (proc_area) { + Host2Mac_memcpy(proc_area, proc, sizeof(proc)); + Execute68k(proc_area, &r); + + fontCount = r.d[0]; + + r.a[0] = proc_area; + Execute68kTrap(0xa01f, &r); // DisposePtr + } + + if (fontCount < 0) { + fontCount = 0; + } + + return fontCount; +} + +/* + * Get Mac font ID at index + */ + +static int16_t MacFontIDAtIndex(NSUInteger index) +{ + M68kRegisters r; + static uint8_t get_res_handle_proc[] = { + 0x42, 0x27, // clr.b -(sp) + 0xa9, 0x9b, // SetResLoad() + 0x59, 0x4f, // subq.w #4,sp + 0x2f, 0x3c, 'F', 'O', 'N', 'D', // move.l #'FOND',-(sp) + 0x3f, 0x3c, 0, 0, // move.w #index,-(sp) + 0xa9, 0x9d, // GetIndResource() + 0x26, 0x5f, // movea.l (sp)+,A3 + 0x1f, 0x3c, 0x00, 0x01, // move.b #1,-(sp) + 0xa9, 0x9b, // SetResLoad() + M68K_RTS >> 8, M68K_RTS & 0xff + }; + r.d[0] = sizeof(get_res_handle_proc); + Execute68kTrap(0xa71e, &r); // NewPtrSysClear() + uint32_t proc_area = r.a[0]; + + uint32_t res_handle = 0; + int16_t fontID = 0; + + if (proc_area) { + Host2Mac_memcpy(proc_area, get_res_handle_proc, sizeof(get_res_handle_proc)); + WriteMacInt16(proc_area + 14, (uint16_t)(index + 1)); + + Execute68k(proc_area, &r); + + res_handle = r.a[3]; + + r.a[0] = proc_area; + Execute68kTrap(0xa01f, &r); // DisposePtr() + } + + if (res_handle) { + static uint8_t get_info_proc[] = { + 0x2f, 0x0a, // move.l A2,-(sp) + 0x2f, 0x0b, // move.l A3,-(sp) + 0x42, 0xa7, // clr.l -(sp) + 0x42, 0xa7, // clr.l -(sp) + 0xa9, 0xa8, // GetResInfo() + 0x2f, 0x0a, // move.l A2,-(sp) + 0xa9, 0xa3, // ReleaseResource() + M68K_RTS >> 8, M68K_RTS & 0xff, + 0, 0 + }; + + r.d[0] = sizeof(get_info_proc); + Execute68kTrap(0xa71e, &r); // NewPtrSysClear() + proc_area = r.a[0]; + + if (proc_area) { + Host2Mac_memcpy(proc_area, get_info_proc, sizeof(get_info_proc)); + r.a[2] = res_handle; + r.a[3] = proc_area + 16; + + Execute68k(proc_area, &r); + + fontID = ReadMacInt16(proc_area + 16); + + r.a[0] = proc_area; + Execute68kTrap(0xa01f, &r); // DisposePtr() + } + } + + return fontID; +} + +/* + * List all font IDs on the system + */ + +static NSArray *ListMacFonts() +{ + NSUInteger fontCount = CountMacFonts(); + NSMutableArray *fontIDs = [NSMutableArray array]; + + for (NSUInteger i = 0; i < fontCount; i++) { + int16_t eachFontID = MacFontIDAtIndex(i); + + [fontIDs addObject:[NSNumber numberWithShort:eachFontID]]; + } + + return fontIDs; +} + +/* + * List all font IDs having a certain script + */ + +static NSArray *ListMacFontsForScript(ScriptCode script) +{ + NSMutableArray *fontIDs = [NSMutableArray array]; + + for (NSNumber *eachFontIDNum in ListMacFonts()) { + if (ScriptNumberForFontID([eachFontIDNum shortValue]) == script) + [fontIDs addObject:eachFontIDNum]; + } + + return fontIDs; } /* @@ -210,12 +447,12 @@ static NSString *FontNameFromFontID(int16_t fontID) uint8_t * const namePtr = Mac2HostAddr(name_area); - CFStringRef name = CFStringCreateWithPascalString(kCFAllocatorDefault, namePtr, kCFStringEncodingMacRoman); + NSString *name = (NSString *)CFStringCreateWithPascalString(kCFAllocatorDefault, namePtr, kCFStringEncodingMacRoman); r.a[0] = name_area; Execute68kTrap(0xa01f, &r); // DisposePtr - return [(NSString *)name autorelease]; + return [name autorelease]; } /* @@ -269,12 +506,42 @@ static int16_t FontIDFromFontName(NSString *fontName) } /* - * Convert Mac styl to attributed string + * Get font ID in desired script if possible; otherwise, try to get some font in the desired script. */ -static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *stylData) +static int16_t FontIDFromFontNameAndScript(NSString *fontName, ScriptCode script) { - NSMutableAttributedString *aStr = [[[NSMutableAttributedString alloc] initWithString:string] autorelease]; + int16_t fontID = FontIDFromFontName(fontName); + + if (ScriptNumberForFontID(fontID) == script) + return fontID; + + NSArray *fontIDs = ListMacFontsForScript(script); + + if ([fontIDs count] == 0) + return fontID; // no fonts are going to work; might as well return the original one + + if (fontName) { + // look for a localized version of our font; e.g. "Helvetica CE" if our font is Helvetica + for (NSNumber *eachFontIDNum in fontIDs) { + int16_t eachFontID = [eachFontIDNum shortValue]; + + if ([FontNameFromFontID(eachFontID) hasPrefix:fontName]) + return eachFontID; + } + } + + // Give up and just return a font that will work + return [[fontIDs objectAtIndex:0] shortValue]; +} + +/* + * Convert Mac TEXT/styl to attributed string + */ + +static NSAttributedString *AttributedStringFromMacTEXTAndStyl(NSData *textData, NSData *stylData) +{ + NSMutableAttributedString *aStr = [[[NSMutableAttributedString alloc] init] autorelease]; if (aStr == nil) return nil; @@ -291,12 +558,12 @@ static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *s if (length < elements * elementSize) return nil; - CFIndex pointer = 2; + NSUInteger pointer = 2; - for (NSUInteger i = 0; i < elements; i++) { + for (NSUInteger i = 0; i < elements; i++) AUTORELEASE_POOL { int32_t startChar = CFSwapInt32BigToHost(*(int32_t *)(bytes + pointer)); pointer += 4; - int16_t height = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; - int16_t ascent = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; + int16_t height __attribute__((unused)) = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; + int16_t ascent __attribute__((unused)) = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; int16_t fontID = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; uint8_t face = bytes[pointer]; pointer += 2; int16_t size = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2; @@ -307,13 +574,14 @@ static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *s int32_t nextChar; if (i + 1 == elements) - nextChar = [aStr length]; + nextChar = [textData length]; else nextChar = CFSwapInt32BigToHost(*(int32_t *)(bytes + pointer)); NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; NSColor *color = [NSColor colorWithDeviceRed:(CGFloat)red / 65535.0 green:(CGFloat)green / 65535.0 blue:(CGFloat)blue / 65535.0 alpha:1.0]; NSFont *font; + TextEncoding encoding; if (fontID == 0) { // System font CGFloat fontSize = (size == 0) ? [NSFont systemFontSize] : (CGFloat)size; @@ -323,11 +591,25 @@ static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *s } else { NSString *fontName = FontNameFromFontID(fontID); font = [NSFont fontWithName:fontName size:(CGFloat)size]; + + if (font == nil) { + // Convert localized variants of fonts; e.g. "Helvetica CE" to "Helvetica" + + NSRange wsRange = [fontName rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet] options:NSBackwardsSearch]; + + if (wsRange.length) { + fontName = [fontName substringToIndex:wsRange.location]; + font = [NSFont fontWithName:fontName size:(CGFloat)size]; + } + } } if (font == nil) font = [NSFont userFontOfSize:(CGFloat)size]; + if (UpgradeScriptInfoToTextEncoding(ScriptNumberForFontID(fontID), kTextLanguageDontCare, kTextRegionDontCare, NULL, &encoding)) + encoding = MacDefaultTextEncoding(); + NSFontManager *fm = [NSFontManager sharedFontManager]; if (face & FONT_FACE_BOLD) @@ -366,198 +648,204 @@ static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *s [attrs setObject:color forKey:NSForegroundColorAttributeName]; - [aStr setAttributes:attrs range:NSMakeRange(startChar, nextChar - startChar)]; + NSData *partialData = [textData subdataWithRange:NSMakeRange(startChar, nextChar - startChar)]; + NSString *partialString = [[NSString alloc] initWithData:partialData encoding:CFStringConvertEncodingToNSStringEncoding(encoding)]; + if (partialString) { + NSAttributedString *partialAttribString = [[NSAttributedString alloc] initWithString:partialString attributes:attrs]; + + [aStr appendAttributedString:partialAttribString]; + + [partialAttribString release]; + } + + [partialString release]; [attrs release]; } return aStr; } +/* + * Append styl data for one text run + */ + +static void AppendStylRunData(NSMutableData *stylData, NSDictionary *attrs, ScriptCode script) +{ + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; + + NSFont *font = [attrs objectForKey:NSFontAttributeName]; + NSColor *color = [[attrs objectForKey:NSForegroundColorAttributeName] colorUsingColorSpaceName:NSDeviceRGBColorSpace device:nil]; + NSFontTraitMask traits = [fontManager traitsOfFont:font]; + NSNumber *underlineStyle = [attrs objectForKey:NSUnderlineStyleAttributeName]; + NSNumber *strokeWidth = [attrs objectForKey:NSStrokeWidthAttributeName]; + NSShadow *shadow = [attrs objectForKey:NSShadowAttributeName]; + + int16_t hostFontID = FontIDFromFontNameAndScript([font familyName], script); + + if (hostFontID == 0) { + hostFontID = [font isFixedPitch] ? 4 /* Monaco */ : 1 /* Application font */; + } + + int16_t height = CFSwapInt16HostToBig((int16_t)rint([layoutManager defaultLineHeightForFont:font])); + int16_t ascent = CFSwapInt16HostToBig((int16_t)rint([font ascender])); + int16_t fontID = CFSwapInt16HostToBig(hostFontID); + uint8_t face = 0; + int16_t size = CFSwapInt16HostToBig((int16_t)rint([font pointSize])); + uint16_t red = CFSwapInt16HostToBig((int16_t)rint([color redComponent] * 65535.0)); + uint16_t green = CFSwapInt16HostToBig((int16_t)rint([color greenComponent] * 65535.0)); + uint16_t blue = CFSwapInt16HostToBig((int16_t)rint([color blueComponent] * 65535.0)); + + if (traits & NSBoldFontMask) { + face |= FONT_FACE_BOLD; + } + + if (traits & NSItalicFontMask) { + face |= FONT_FACE_ITALIC; + } + + if (traits & NSCondensedFontMask) { + face |= FONT_FACE_CONDENSED; + } + + if (traits & NSExpandedFontMask) { + face |= FONT_FACE_EXTENDED; + } + + if (underlineStyle && [underlineStyle integerValue] != NSUnderlineStyleNone) { + face |= FONT_FACE_UNDERLINE; + } + + if (strokeWidth && [strokeWidth doubleValue] > 0.0) { + face |= FONT_FACE_OUTLINE; + } + + if (shadow) { + face |= FONT_FACE_SHADOW; + } + + [stylData appendBytes:&height length:2]; + [stylData appendBytes:&ascent length:2]; + [stylData appendBytes:&fontID length:2]; + [stylData appendBytes:&face length:1]; + [stylData increaseLengthBy:1]; + [stylData appendBytes:&size length:2]; + [stylData appendBytes:&red length:2]; + [stylData appendBytes:&green length:2]; + [stylData appendBytes:&blue length:2]; + + [layoutManager release]; +} + /* * Convert attributed string to TEXT/styl */ -static NSData *ConvertToMacTEXTAndStyl(NSAttributedString *aStr, NSData **outStylData) { +static NSData *ConvertToMacTEXTAndStyl(NSAttributedString *aStr, NSData **outStylData) +{ // Limitations imposed by the Mac TextEdit system. - // Something to test would be whether using UTF16 causes TextEdit to choke at 16K characters - // instead of 32K characters, depending on whether this is a byte limit or a true character limit. - // If the former, UTF8 might be a better choice for encoding here. const NSUInteger charLimit = 32 * 1024; const NSUInteger elementLimit = 1601; - - if ([aStr length] > charLimit) { + + NSUInteger length = [aStr length]; + + if (length > charLimit) { aStr = [aStr attributedSubstringFromRange:NSMakeRange(0, charLimit)]; } - - // See comment in CreateRTFDataFromMacTEXTAndStyl regarding encodings; I hope I've interpreted - // the existing code correctly in this regard -#if __LITTLE_ENDIAN__ - NSStringEncoding encoding = NSUTF16LittleEndianStringEncoding; -#else - NSStringEncoding encoding = NSUTF16BigEndianStringEncoding; -#endif - - NSData *textData = [[aStr string] dataUsingEncoding:encoding]; + + NSArray *runs = nil; + + NSData *textData = ConvertToMacTextEncoding(aStr, &runs); + NSMutableData *stylData = [NSMutableData dataWithLength:2]; // number of styles to be filled in at the end - - NSUInteger length = [aStr length]; + NSUInteger elements = 0; - - NSFontManager *fontManager = [NSFontManager sharedFontManager]; - NSLayoutManager *layoutManager = [[[NSLayoutManager alloc] init] autorelease]; - - for (NSUInteger index = 0; index < length && elements < elementLimit;) { - NSRange attrRange; - NSDictionary *attrs = [aStr attributesAtIndex:index effectiveRange:&attrRange]; - - NSFont *font = [attrs objectForKey:NSFontAttributeName]; - NSColor *color = [[attrs objectForKey:NSForegroundColorAttributeName] colorUsingColorSpaceName:NSDeviceRGBColorSpace device:nil]; - NSFontTraitMask traits = [fontManager traitsOfFont:font]; - NSNumber *underlineStyle = [attrs objectForKey:NSUnderlineStyleAttributeName]; - NSNumber *strokeWidth = [attrs objectForKey:NSStrokeWidthAttributeName]; - NSShadow *shadow = [attrs objectForKey:NSShadowAttributeName]; - - int16_t hostFontID = FontIDFromFontName([font familyName]); - - if (hostFontID == 0) { - hostFontID = [font isFixedPitch] ? 4 /* Monaco */ : 1 /* Application font */; - } - - int32_t startChar = CFSwapInt32HostToBig((int32_t)index); - int16_t height = CFSwapInt16HostToBig((int16_t)rint([layoutManager defaultLineHeightForFont:font])); - int16_t ascent = CFSwapInt16HostToBig((int16_t)rint([font ascender])); - int16_t fontID = CFSwapInt16HostToBig(hostFontID); - uint8_t face = 0; - int16_t size = CFSwapInt16HostToBig((int16_t)rint([font pointSize])); - uint16_t red = CFSwapInt16HostToBig((int16_t)rint([color redComponent] * 65535.0)); - uint16_t green = CFSwapInt16HostToBig((int16_t)rint([color greenComponent] * 65535.0)); - uint16_t blue = CFSwapInt16HostToBig((int16_t)rint([color blueComponent] * 65535.0)); - - if (traits & NSBoldFontMask) { - face |= FONT_FACE_BOLD; - } - - if (traits & NSItalicFontMask) { - face |= FONT_FACE_ITALIC; - } - - if (traits & NSCondensedFontMask) { - face |= FONT_FACE_CONDENSED; - } - - if (traits & NSExpandedFontMask) { - face |= FONT_FACE_EXTENDED; - } - - if (underlineStyle && [underlineStyle integerValue] != NSUnderlineStyleNone) { - face |= FONT_FACE_UNDERLINE; - } - - if (strokeWidth && [strokeWidth doubleValue] > 0.0) { - face |= FONT_FACE_OUTLINE; - } - - if (shadow) { - face |= FONT_FACE_SHADOW; - } - + + for (NSDictionary *eachRun in runs) { + if (elements >= elementLimit) + break; + + NSUInteger offset = [[eachRun objectForKey:@"offset"] unsignedIntegerValue]; + ScriptCode script = [[eachRun objectForKey:@"script"] shortValue]; + NSDictionary *attrs = [eachRun objectForKey:@"attributes"]; + + int32_t startChar = CFSwapInt32HostToBig((int32_t)offset); [stylData appendBytes:&startChar length:4]; - [stylData appendBytes:&height length:2]; - [stylData appendBytes:&ascent length:2]; - [stylData appendBytes:&fontID length:2]; - [stylData appendBytes:&face length:1]; - [stylData increaseLengthBy:1]; - [stylData appendBytes:&size length:2]; - [stylData appendBytes:&red length:2]; - [stylData appendBytes:&green length:2]; - [stylData appendBytes:&blue length:2]; - - index += attrRange.length; + + AppendStylRunData(stylData, attrs, script); + elements++; } - + uint16_t bigEndianElements = CFSwapInt16HostToBig((uint16_t)elements); - + [stylData replaceBytesInRange:NSMakeRange(0, 2) withBytes:&bigEndianElements length:2]; - + if (outStylData) *outStylData = stylData; - - textData = (NSData *)ConvertMacTextEncoding((CFDataRef)[textData retain], YES); - - return [textData autorelease]; + + return textData; } /* * Convert Mac TEXT/styl to RTF */ -static CFDataRef CreateRTFDataFromMacTEXTAndStyl(CFDataRef textData, CFDataRef stylData) +static void WriteMacTEXTAndStylToPasteboard(NSData *textData, NSData *stylData) { - // Unfortunately, CF does not seem to have any RTF conversion routines, so do this in Cocoa instead. - // If we are willing to require OS X 10.6 minimum, we should use the NSPasteboardWriting methods - // instead of putting the RTF data up ourselves. - - NSData *rtfData = nil; - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - // I think this is what's going on? ConvertMacTextEncoding() converts to Unicode in host endian? - // Maybe UTF8 would be a less ambiguous form to store the pasteboard data in? - -#if __LITTLE_ENDIAN__ - NSStringEncoding encoding = NSUTF16LittleEndianStringEncoding; -#else - NSStringEncoding encoding = NSUTF16BigEndianStringEncoding; -#endif - - NSMutableString *string = [[[NSMutableString alloc] initWithData:(NSData *)textData encoding:encoding] autorelease]; - - // fix line endings - [string replaceOccurrencesOfString:@"\r" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [string length])]; - - if (string != nil) { - NSAttributedString *aStr = ConvertToAttributedString(string, (NSData *)stylData); - - rtfData = [[aStr RTFFromRange:NSMakeRange(0, [aStr length]) documentAttributes:nil] retain]; + NSMutableAttributedString *aStr = [AttributedStringFromMacTEXTAndStyl(textData, stylData) mutableCopy]; + + if (!aStr) { + NSString *string = [[NSString alloc] initWithData:textData encoding:CFStringConvertEncodingToNSStringEncoding(MacDefaultTextEncoding())]; + + if (!string) + return; + + aStr = [[NSMutableAttributedString alloc] initWithString:string attributes:nil]; + + [string release]; } - - [pool drain]; - - return (CFDataRef)rtfData; + + // fix line endings + [[aStr mutableString] replaceOccurrencesOfString:@"\r" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [aStr length])]; + + [g_pboard writeObjects:[NSArray arrayWithObject:aStr]]; } /* * Convert RTF to Mac TEXT/styl */ -static CFDataRef CreateMacTEXTAndStylFromRTFData(CFDataRef rtfData, CFDataRef *outStylData) +static NSData *GetMacTEXTAndStylDataFromPasteboard(NSData **outStylData) { - // No easy way to do this at the CF layer, so use Cocoa. - // Reading RTF should be backward compatible to the early releases of OS X; - // if we are willing to require OS X 10.6 or better, we should use NSPasteboardReading - // to read an NSAttributedString off of the pasteboard, which will give us the ability - // to potentially read more rich-text formats than RTF. - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSMutableAttributedString *aStr = [[[NSMutableAttributedString alloc] initWithRTF:(NSData *)rtfData documentAttributes:nil] autorelease]; - + NSMutableAttributedString *aStr; + + NSArray *objs = [g_pboard readObjectsForClasses:[NSArray arrayWithObject:[NSAttributedString class]] options:nil]; + + if ([objs count]) { + aStr = [[objs objectAtIndex:0] mutableCopy]; + } else { + objs = [g_pboard readObjectsForClasses:[NSArray arrayWithObject:[NSString class]] options:nil]; + + if (![objs count]) + return nil; + + aStr = [[NSMutableAttributedString alloc] initWithString:[objs objectAtIndex:0]]; + } + // fix line endings [[aStr mutableString] replaceOccurrencesOfString:@"\n" withString:@"\r" options:NSLiteralSearch range:NSMakeRange(0, [[aStr mutableString] length])]; - + NSData *stylData = nil; NSData *textData = ConvertToMacTEXTAndStyl(aStr, &stylData); - - [textData retain]; - + + [aStr release]; + if (outStylData) - *outStylData = (CFDataRef)[stylData retain]; - - [pool drain]; - - return (CFDataRef)textData; + *outStylData = stylData; + + return textData; } /* @@ -566,11 +854,12 @@ static CFDataRef CreateMacTEXTAndStylFromRTFData(CFDataRef rtfData, CFDataRef *o void ClipInit(void) { - OSStatus err = PasteboardCreate(kPasteboardClipboard, &g_pbref); - if (err) { + g_pboard = [[NSPasteboard generalPasteboard] retain]; + if (!g_pboard) { D(bug("could not create Pasteboard\n")); - g_pbref = NULL; } + + macScrap = [[NSMutableDictionary alloc] init]; } @@ -580,36 +869,11 @@ void ClipInit(void) void ClipExit(void) { - if (g_pbref) { - CFRelease(g_pbref); - g_pbref = NULL; - } -} + [g_pboard release]; + g_pboard = nil; -/* - * Copy data from the host pasteboard - */ - -static CFDataRef CopyPasteboardDataWithFlavor(CFStringRef flavor) -{ - ItemCount itemCount; - - if (PasteboardGetItemCount(g_pbref, &itemCount)) - return NULL; - - for (UInt32 itemIndex = 1; itemIndex <= itemCount; itemIndex++) { - PasteboardItemID itemID; - CFDataRef pbData; - - if (PasteboardGetItemIdentifier(g_pbref, itemIndex, &itemID)) - break; - - if (!PasteboardCopyItemFlavorData(g_pbref, itemID, flavor, &pbData)) { - return pbData; - } - } - - return NULL; + [macScrap release]; + macScrap = nil; } /* @@ -618,9 +882,9 @@ static CFDataRef CopyPasteboardDataWithFlavor(CFStringRef flavor) static void ZeroMacClipboard() { - D(bug(stderr, "Zeroing Mac clipboard\n")); + D(bug("Zeroing Mac clipboard\n")); M68kRegisters r; - static uint8 proc[] = { + static uint8_t proc[] = { 0x59, 0x8f, // subq.l #4,sp 0xa9, 0xfc, // ZeroScrap() 0x58, 0x8f, // addq.l #4,sp @@ -628,12 +892,12 @@ static void ZeroMacClipboard() }; r.d[0] = sizeof(proc); Execute68kTrap(0xa71e, &r); // NewPtrSysClear() - uint32 proc_area = r.a[0]; - + uint32_t proc_area = r.a[0]; + if (proc_area) { Host2Mac_memcpy(proc_area, proc, sizeof(proc)); Execute68k(proc_area, &r); - + r.a[0] = proc_area; Execute68kTrap(0xa01f, &r); // DisposePtr } @@ -643,25 +907,25 @@ static void ZeroMacClipboard() * Write data to Mac clipboard */ -static void WriteDataToMacClipboard(CFDataRef pbData, uint32 type) +static void WriteDataToMacClipboard(NSData *pbData, uint32_t type) { - D(bug(stderr, "Writing data %s to Mac clipboard with type '%c%c%c%c'\n", [[(NSData *)pbData description] UTF8String], - (type >> 24) & 0xff, (type >> 16) & 0xff, (type >> 8) & 0xff, type & 0xff)); - + D(bug("Writing data %s to Mac clipboard with type '%c%c%c%c'\n", [[pbData description] UTF8String], + (type >> 24) & 0xff, (type >> 16) & 0xff, (type >> 8) & 0xff, type & 0xff)); + // Allocate space for new scrap in MacOS side M68kRegisters r; - r.d[0] = CFDataGetLength(pbData); + r.d[0] = [pbData length]; Execute68kTrap(0xa71e, &r); // NewPtrSysClear() - uint32 scrap_area = r.a[0]; - + uint32_t scrap_area = r.a[0]; + // Get the native clipboard data if (scrap_area) { - uint8 * const data = Mac2HostAddr(scrap_area); - - memcpy(data, CFDataGetBytePtr(pbData), CFDataGetLength(pbData)); - + uint8_t * const data = Mac2HostAddr(scrap_area); + + memcpy(data, [pbData bytes], [pbData length]); + // Add new data to clipboard - static uint8 proc[] = { + static uint8_t proc[] = { 0x59, 0x8f, // subq.l #4,sp 0x2f, 0x3c, 0, 0, 0, 0, // move.l #length,-(sp) 0x2f, 0x3c, 0, 0, 0, 0, // move.l #type,-(sp) @@ -672,20 +936,20 @@ static void WriteDataToMacClipboard(CFDataRef pbData, uint32 type) }; r.d[0] = sizeof(proc); Execute68kTrap(0xa71e, &r); // NewPtrSysClear() - uint32 proc_area = r.a[0]; - + uint32_t proc_area = r.a[0]; + if (proc_area) { Host2Mac_memcpy(proc_area, proc, sizeof(proc)); - WriteMacInt32(proc_area + 4, CFDataGetLength(pbData)); + WriteMacInt32(proc_area + 4, [pbData length]); WriteMacInt32(proc_area + 10, type); WriteMacInt32(proc_area + 16, scrap_area); we_put_this_data = true; Execute68k(proc_area, &r); - + r.a[0] = proc_area; Execute68kTrap(0xa01f, &r); // DisposePtr } - + r.a[0] = scrap_area; Execute68kTrap(0xa01f, &r); // DisposePtr } @@ -695,55 +959,52 @@ static void WriteDataToMacClipboard(CFDataRef pbData, uint32 type) * Mac application reads clipboard */ -void GetScrap(void **handle, uint32 type, int32 offset) +void GetScrap(void **handle, uint32_t type, int32_t offset) { D(bug("GetScrap handle %p, type %4.4s, offset %d\n", handle, (char *)&type, offset)); - - CFStringRef typeStr; - PasteboardSyncFlags syncFlags; - - if (!g_pbref) - return; - - syncFlags = PasteboardSynchronize(g_pbref); - if (syncFlags & kPasteboardModified) - return; - - if (!(typeStr = GetUTIFromFlavor(type))) - return; - - if (type == TYPE_TEXT || type == TYPE_STYL) { - CFDataRef rtfData = CopyPasteboardDataWithFlavor(kUTTypeRTF); - - if (rtfData != NULL) { - CFDataRef stylData = NULL; - CFDataRef textData = CreateMacTEXTAndStylFromRTFData(rtfData, &stylData); - - ZeroMacClipboard(); - - if (stylData) - WriteDataToMacClipboard(stylData, TYPE_STYL); - - WriteDataToMacClipboard(textData, TYPE_TEXT); - - CFRelease(textData); - CFRelease(stylData); - CFRelease(rtfData); - + + AUTORELEASE_POOL { + NSString *typeStr; + + if (!g_pboard) return; + + if (!(typeStr = GetUTIFromFlavor(type))) + return; + + if (type == TYPE_TEXT || type == TYPE_STYL) { + NSData *stylData = nil; + NSData *textData = GetMacTEXTAndStylDataFromPasteboard(&stylData); + + if (textData) { + ZeroMacClipboard(); + + if (stylData) + WriteDataToMacClipboard(stylData, TYPE_STYL); + + WriteDataToMacClipboard(textData, TYPE_TEXT); + + return; + } + } + + NSData *pbData = nil; + + NSArray *objs = [g_pboard readObjectsForClasses:[NSArray arrayWithObject:[NSPasteboardItem class]] options:nil]; + + for (NSPasteboardItem *eachItem in objs) { + NSData *data = [eachItem dataForType:typeStr]; + + if ([data length]) { + pbData = data; + break; + } + } + + if (pbData) { + ZeroMacClipboard(); + WriteDataToMacClipboard(pbData, type); } - } - - CFDataRef pbData = CopyPasteboardDataWithFlavor(typeStr); - - if (pbData) { - if (type == TYPE_TEXT) - pbData = ConvertMacTextEncoding(pbData, TRUE); - - ZeroMacClipboard(); - WriteDataToMacClipboard(pbData, type); - - CFRelease(pbData); } } @@ -754,9 +1015,9 @@ void GetScrap(void **handle, uint32 type, int32 offset) void ZeroScrap() { D(bug("ZeroScrap\n")); - + we_put_this_data = false; - + // Defer clearing the host pasteboard until the Mac tries to put something on it. // This prevents us from clearing the pasteboard when ZeroScrap() is called during startup. should_clear = true; @@ -766,71 +1027,55 @@ void ZeroScrap() * Mac application wrote to clipboard */ -void PutScrap(uint32 type, void *scrap, int32 length) +void PutScrap(uint32_t type, void *scrap, int32_t length) { D(bug("PutScrap type %4.4s, data %p, length %ld\n", (char *)&type, scrap, (long)length)); - - PasteboardSyncFlags syncFlags; - CFStringRef typeStr; - - if (!g_pbref) - return; - - if (!(typeStr = GetUTIFromFlavor(type))) - return; - - if (we_put_this_data) { - we_put_this_data = false; - return; - } - if (length <= 0) - return; - - if (should_clear) { - PasteboardClear(g_pbref); - should_clear = false; - } - - syncFlags = PasteboardSynchronize(g_pbref); - if ((syncFlags & kPasteboardModified) || !(syncFlags & kPasteboardClientIsOwner)) - return; - - CFDataRef pbData = CFDataCreate(kCFAllocatorDefault, (UInt8*)scrap, length); - if (!pbData) - return; - - - if (type == TYPE_TEXT) - pbData = ConvertMacTextEncoding(pbData, FALSE); - - if (pbData) { - PasteboardPutItemFlavor(g_pbref, (PasteboardItemID)1, typeStr, pbData, 0); - CFRelease(pbData); - } - - if (type == TYPE_TEXT || type == TYPE_STYL) { - CFDataRef textData; - CFDataRef stylData; - if (PasteboardCopyItemFlavorData(g_pbref, (PasteboardItemID)1, TEXT_FLAVOR_NAME, &textData) != noErr) - textData = NULL; + AUTORELEASE_POOL { + NSString *typeStr; - if (PasteboardCopyItemFlavorData(g_pbref, (PasteboardItemID)1, STYL_FLAVOR_NAME, &stylData) != noErr) - stylData = NULL; + if (!g_pboard) + return; - if (textData != NULL && stylData != NULL) { - CFDataRef rtfData = CreateRTFDataFromMacTEXTAndStyl(textData, stylData); + if (!(typeStr = GetUTIFromFlavor(type))) + return; - if (rtfData) { - PasteboardPutItemFlavor(g_pbref, (PasteboardItemID)1, kUTTypeRTF, rtfData, 0); - CFRelease(rtfData); - } + if (we_put_this_data) { + we_put_this_data = false; + return; + } + if (length <= 0) + return; + + if (should_clear) { + [g_pboard clearContents]; + + [macScrap removeAllObjects]; + should_clear = false; } - if (textData) - CFRelease(textData); + NSData *pbData = [NSData dataWithBytes:scrap length:length]; + if (!pbData) + return; - if (stylData) - CFRelease(stylData); + [macScrap setObject:pbData forKey:[NSNumber numberWithInteger:type]]; + + if (type == TYPE_TEXT || type == TYPE_STYL) { + NSData *textData; + NSData *stylData; + + textData = [macScrap objectForKey:[NSNumber numberWithInteger:TYPE_TEXT]]; + stylData = [macScrap objectForKey:[NSNumber numberWithInteger:TYPE_STYL]]; + + if (textData) { + WriteMacTEXTAndStylToPasteboard(textData, stylData); + } + } else if (pbData) { + NSPasteboardItem *pbItem = [[[NSPasteboardItem alloc] init] autorelease]; + + [pbItem setData:pbData forType:typeStr]; + + [g_pboard writeObjects:[NSArray arrayWithObject:pbItem]]; + } } } diff --git a/SheepShaver/src/MacOSX/autorelease.h b/SheepShaver/src/MacOSX/autorelease.h new file mode 120000 index 00000000..154cdead --- /dev/null +++ b/SheepShaver/src/MacOSX/autorelease.h @@ -0,0 +1 @@ +../../../BasiliskII/src/MacOSX/autorelease.h \ No newline at end of file