Patch for copying and pasting styled text in Basilisk II / SheepShaver

Added code to parse the Classic Mac OS 'styl' resources, allowing formatted text to be copied and pasted out of SheepShaver, not just plain text. In order to do this, I made some changes to the emul_op mechanism, patching ZeroScrap() in addition to the scrap methods that were already being patched. The reason for this is that since we need to read data from multiple items that are on the clipboard at once, we cannot simply assume a zero at the beginning of each PutScrap() operation.

This patch uses RTF to store styled text on the host side; unfortunately, since the APIs to convert to and from RTF data are in Cocoa but not in CoreFoundation, I had to write the new portions in Objective-C rather than C, and changed the extension from .cpp to .mm accordingly. In the future, if we are confident that this file will only be used on Mac OS X 10.6 and up, we can rewrite the Pasteboard Manager code to use NSPasteboardReading/Writing instead. This would allow us to read and write NSAttributedString objects directly to and from the pasteboard, which would make sure we were always using the OS's preferred rich text format internally instead of hard-coding it specifically to RTF as in the current implementation.

I believe that this patch should also fix the problem Ronald reported with copying accented characters.

Since I am new to 68k assembly and the emul_op mechanism, I would appreciate if someone could double-check all my changes to make sure that I have done everything correctly.

Thanks,
Charles
This commit is contained in:
CharlesJS 2012-06-29 17:53:13 -05:00 committed by Alexei Svitkine
parent 3fc0e4dd98
commit ed28705ee3
10 changed files with 676 additions and 94 deletions

View File

@ -97,6 +97,14 @@ void ClipExit(void)
FreeIFF(iffw);
}
/*
* Mac application zeroes clipboard
*/
void ZeroScrap()
{
}
/*
* Mac application reads clipboard

View File

@ -52,6 +52,14 @@ void ClipExit(void)
{
}
/*
* Mac application zeroes clipboard
*/
void ZeroScrap()
{
}
/*
* Mac application reads clipboard

View File

@ -3,6 +3,7 @@
*
* (C) 2012 Jean-Pierre Stierlin
* (C) 2012 Alexei Svitkine
* (C) 2012 Charles Srstka
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,6 +22,7 @@
#include "sysdeps.h"
#define _UINT64
#import <Cocoa/Cocoa.h>
#include <ApplicationServices/ApplicationServices.h>
#include "clip.h"
@ -37,23 +39,41 @@
#define TYPE_PICT FOURCC('P','I','C','T')
#define TYPE_TEXT FOURCC('T','E','X','T')
#define TYPE_STYL FOURCC('s','t','y','l')
static PasteboardRef g_pbref;
// 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");
enum {
FONT_FACE_PLAIN = 0,
FONT_FACE_BOLD = 1,
FONT_FACE_ITALIC = 2,
FONT_FACE_UNDERLINE = 4,
FONT_FACE_OUTLINE = 8,
FONT_FACE_SHADOW = 16,
FONT_FACE_CONDENSED = 32,
FONT_FACE_EXTENDED = 64
};
static CFStringRef GetUTIFromFlavor(uint32 type)
{
switch (type) {
case TYPE_PICT: return kUTTypePICT;
case TYPE_TEXT: return CFSTR("com.apple.traditional-mac-plain-text");
//case TYPE_TEXT: return CFSTR("public.utf16-plain-text");
//case FOURCC('s','t','y','l'): return CFSTR("????");
case TYPE_TEXT: return 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 CFSTR("public.utf16-plain-text");
case FOURCC('u','t','1','6'): return CFSTR("public.utf16-plain-text");
//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;
@ -97,7 +117,7 @@ static int GetMacScriptManagerVariable(uint16 id)
* Convert utf-16 from/to system script encoding on Mac
*/
CFDataRef ConvertMacTextEncoding(CFDataRef pbData, int from_host)
static CFDataRef ConvertMacTextEncoding(CFDataRef pbData, int from_host)
{
static TextEncoding g_textEncodingHint = kTextEncodingUnknown;
static UnicodeMapping uMapping;
@ -124,21 +144,21 @@ CFDataRef ConvertMacTextEncoding(CFDataRef pbData, int from_host)
LogicalAddress outBytesPtr = malloc(byteCount * 2);
if (!outBytesPtr)
return pbData;
ByteCount outBytesConverted;
OSStatus err;
if (from_host) {
err = ConvertFromUnicodeToText(utInfo, byteCount, (UniChar *)CFDataGetBytePtr(pbData),
kUnicodeLooseMappingsMask,
0, NULL, 0, NULL,
outBytesLength,
&outBytesConverted, &outBytesLength, outBytesPtr);
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);
kUnicodeLooseMappingsMask,
0, NULL, 0, NULL,
outBytesLength,
&outBytesConverted, &outBytesLength, (UniChar *)outBytesPtr);
}
if (err == noErr && outBytesConverted == byteCount) {
@ -153,6 +173,393 @@ CFDataRef ConvertMacTextEncoding(CFDataRef pbData, int from_host)
return pbData;
}
/*
* Convert Mac font ID to font name
*/
static NSString *FontNameFromFontID(int16_t fontID)
{
M68kRegisters r;
r.d[0] = 256; // Str255: 255 characters + length byte
Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
uint32_t name_area = r.a[0];
if (!name_area)
return nil;
uint8_t proc[] = {
0x3f, 0x3c, 0, 0, // move.w #fontID,-(sp)
0x2f, 0x0a, // move.l A2,-(sp)
0xa8, 0xff, // GetFontName()
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 + 2, fontID);
r.a[2] = name_area;
Execute68k(proc_area, &r);
r.a[0] = proc_area;
Execute68kTrap(0xa01f, &r); // DisposePtr
}
uint8_t * const namePtr = Mac2HostAddr(name_area);
CFStringRef name = CFStringCreateWithPascalString(kCFAllocatorDefault, namePtr, kCFStringEncodingMacRoman);
r.a[0] = name_area;
Execute68kTrap(0xa01f, &r); // DisposePtr
return [(NSString *)name autorelease];
}
/*
* Convert font name to Mac font ID
*/
static int16_t FontIDFromFontName(NSString *fontName)
{
M68kRegisters r;
r.d[0] = 256; // Str255: 255 characters + length byte
Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
uint32_t name_area = r.a[0];
if (!name_area)
return 0;
uint8_t * const namePtr = Mac2HostAddr(name_area);
CFStringGetPascalString((CFStringRef)fontName, namePtr, 256, kCFStringEncodingMacRoman);
uint8_t proc[] = {
0x2f, 0x0a, // move.l A2,-(sp)
0x2f, 0x0b, // move.l A3,-(sp)
0xa9, 0x00, // GetFNum()
M68K_RTS >> 8, M68K_RTS & 0xff,
0, 0
};
r.d[0] = sizeof(proc);
Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
uint32_t proc_area = r.a[0];
int16_t fontID = 0;
if (proc_area) {
Host2Mac_memcpy(proc_area, proc, sizeof(proc));
r.a[2] = name_area;
r.a[3] = proc_area + 8;
Execute68k(proc_area, &r);
fontID = ReadMacInt16(proc_area + 8);
r.a[0] = proc_area;
Execute68kTrap(0xa01f, &r); // DisposePtr
}
r.a[0] = name_area;
Execute68kTrap(0xa01f, &r); // DisposePtr
return fontID;
}
/*
* Convert Mac styl to attributed string
*/
static NSAttributedString *ConvertToAttributedString(NSString *string, NSData *stylData)
{
NSMutableAttributedString *aStr = [[[NSMutableAttributedString alloc] initWithString:string] autorelease];
if (aStr == nil)
return nil;
const uint8_t *bytes = (const uint8_t *)[stylData bytes];
NSUInteger length = [stylData length];
if (length < 2)
return nil;
uint16_t elements = CFSwapInt16BigToHost(*(uint16_t *)bytes);
const NSUInteger elementSize = 20;
if (length < elements * elementSize)
return nil;
CFIndex pointer = 2;
for (NSUInteger i = 0; i < elements; i++) {
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 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;
uint16_t red = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2;
uint16_t green = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2;
uint16_t blue = CFSwapInt16BigToHost(*(int16_t *)&bytes[pointer]); pointer += 2;
int32_t nextChar;
if (i + 1 == elements)
nextChar = [aStr 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;
if (fontID == 0) { // System font
CGFloat fontSize = (size == 0) ? [NSFont systemFontSize] : (CGFloat)size;
font = [NSFont systemFontOfSize:fontSize];
} else if (fontID == 1) { // Application font
font = [NSFont userFontOfSize:(CGFloat)size];
} else {
NSString *fontName = FontNameFromFontID(fontID);
font = [NSFont fontWithName:fontName size:(CGFloat)size];
}
if (font == nil)
font = [NSFont userFontOfSize:(CGFloat)size];
NSFontManager *fm = [NSFontManager sharedFontManager];
if (face & FONT_FACE_BOLD)
font = [fm convertFont:font toHaveTrait:NSBoldFontMask];
if (face & FONT_FACE_ITALIC)
font = [fm convertFont:font toHaveTrait:NSItalicFontMask];
if (face & FONT_FACE_CONDENSED)
font = [fm convertFont:font toHaveTrait:NSCondensedFontMask];
if (face & FONT_FACE_EXTENDED)
font = [fm convertFont:font toHaveTrait:NSExpandedFontMask];
[attrs setObject:font forKey:NSFontAttributeName];
if (face & FONT_FACE_UNDERLINE)
[attrs setObject:[NSNumber numberWithInteger:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
if (face & FONT_FACE_OUTLINE) {
[attrs setObject:color forKey:NSStrokeColorAttributeName];
[attrs setObject:[NSNumber numberWithInteger:3] forKey:NSStrokeWidthAttributeName];
}
if (face & FONT_FACE_SHADOW) {
NSShadow *shadow = [[NSShadow alloc] init];
NSColor *shadowColor = [NSColor colorWithDeviceRed:(CGFloat)red / 65535.0 green:(CGFloat)green / 65535.0 blue:(CGFloat)blue / 65535.0 alpha:0.5];
[shadow setShadowColor:shadowColor];
[shadow setShadowOffset:NSMakeSize(2, -2.0)];
[attrs setObject:shadow forKey:NSShadowAttributeName];
[shadow release];
}
[attrs setObject:color forKey:NSForegroundColorAttributeName];
[aStr setAttributes:attrs range:NSMakeRange(startChar, nextChar - startChar)];
[attrs release];
}
return aStr;
}
/*
* Convert attributed string to TEXT/styl
*/
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) {
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];
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;
}
[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;
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];
}
/*
* Convert Mac TEXT/styl to RTF
*/
static CFDataRef CreateRTFDataFromMacTEXTAndStyl(CFDataRef textData, CFDataRef 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];
}
[pool drain];
return (CFDataRef)rtfData;
}
/*
* Convert RTF to Mac TEXT/styl
*/
static CFDataRef CreateMacTEXTAndStylFromRTFData(CFDataRef rtfData, CFDataRef *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];
// 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];
if (outStylData)
*outStylData = (CFDataRef)[stylData retain];
[pool drain];
return (CFDataRef)textData;
}
/*
* Initialization
*/
@ -179,6 +586,110 @@ void ClipExit(void)
}
}
/*
* 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;
}
/*
* Zero Mac clipboard
*/
static void ZeroMacClipboard()
{
D(bug(stderr, "Zeroing Mac clipboard\n"));
M68kRegisters r;
static uint8 proc[] = {
0x59, 0x8f, // subq.l #4,sp
0xa9, 0xfc, // ZeroScrap()
0x58, 0x8f, // addq.l #4,sp
M68K_RTS >> 8, M68K_RTS & 0xff
};
r.d[0] = sizeof(proc);
Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
uint32 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
}
}
/*
* Write data to Mac clipboard
*/
static void WriteDataToMacClipboard(CFDataRef pbData, uint32 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));
// Allocate space for new scrap in MacOS side
M68kRegisters r;
r.d[0] = CFDataGetLength(pbData);
Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
uint32 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));
// Add new data to clipboard
static uint8 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)
0x2f, 0x3c, 0, 0, 0, 0, // move.l #outbuf,-(sp)
0xa9, 0xfe, // PutScrap()
0x58, 0x8f, // addq.l #4,sp
M68K_RTS >> 8, M68K_RTS & 0xff
};
r.d[0] = sizeof(proc);
Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
uint32 proc_area = r.a[0];
if (proc_area) {
Host2Mac_memcpy(proc_area, proc, sizeof(proc));
WriteMacInt32(proc_area + 4, CFDataGetLength(pbData));
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
}
}
/*
* Mac application reads clipboard
@ -186,86 +697,70 @@ void ClipExit(void)
void GetScrap(void **handle, uint32 type, int32 offset)
{
D(bug("GetScrap handle %p, type %4.4s, offset %d\n", handle, &type, offset));
D(bug("GetScrap handle %p, type %4.4s, offset %d\n", handle, (char *)&type, offset));
CFStringRef typeStr;
PasteboardSyncFlags syncFlags;
ItemCount itemCount;
if (!g_pbref)
return;
syncFlags = PasteboardSynchronize(g_pbref);
if (syncFlags & kPasteboardModified)
return;
if (PasteboardGetItemCount(g_pbref, &itemCount))
return;
if (!(typeStr = GetUTIFromFlavor(type)))
return;
for (UInt32 itemIndex = 1; itemIndex <= itemCount; itemIndex++) {
PasteboardItemID itemID;
CFDataRef pbData;
if (PasteboardGetItemIdentifier(g_pbref, itemIndex, &itemID))
break;
if (!PasteboardCopyItemFlavorData(g_pbref, itemID, typeStr, &pbData)) {
if (type == TYPE_TEXT)
pbData = ConvertMacTextEncoding(pbData, TRUE);
if (pbData) {
// Allocate space for new scrap in MacOS side
M68kRegisters r;
r.d[0] = CFDataGetLength(pbData);
Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
uint32 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));
// Add new data to clipboard
static uint8 proc[] = {
0x59, 0x8f, // subq.l #4,sp
0xa9, 0xfc, // ZeroScrap()
0x2f, 0x3c, 0, 0, 0, 0, // move.l #length,-(sp)
0x2f, 0x3c, 0, 0, 0, 0, // move.l #type,-(sp)
0x2f, 0x3c, 0, 0, 0, 0, // move.l #outbuf,-(sp)
0xa9, 0xfe, // PutScrap()
0x58, 0x8f, // addq.l #4,sp
M68K_RTS >> 8, M68K_RTS & 0xff
};
r.d[0] = sizeof(proc);
Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
uint32 proc_area = r.a[0];
if (type == TYPE_TEXT || type == TYPE_STYL) {
CFDataRef rtfData = CopyPasteboardDataWithFlavor(kUTTypeRTF);
if (rtfData != NULL) {
CFDataRef stylData = NULL;
CFDataRef textData = CreateMacTEXTAndStylFromRTFData(rtfData, &stylData);
if (proc_area) {
Host2Mac_memcpy(proc_area, proc, sizeof(proc));
WriteMacInt32(proc_area + 6, CFDataGetLength(pbData));
WriteMacInt32(proc_area + 12, type);
WriteMacInt32(proc_area + 18, scrap_area);
we_put_this_data = true;
Execute68k(proc_area, &r);
ZeroMacClipboard();
r.a[0] = proc_area;
Execute68kTrap(0xa01f, &r); // DisposePtr
}
if(stylData)
WriteDataToMacClipboard(stylData, TYPE_STYL);
r.a[0] = scrap_area;
Execute68kTrap(0xa01f, &r); // DisposePtr
}
}
CFRelease(pbData);
break;
WriteDataToMacClipboard(textData, TYPE_TEXT);
CFRelease(textData);
CFRelease(stylData);
CFRelease(rtfData);
return;
}
}
CFDataRef pbData = CopyPasteboardDataWithFlavor(typeStr);
if (pbData) {
if (type == TYPE_TEXT)
pbData = ConvertMacTextEncoding(pbData, TRUE);
ZeroMacClipboard();
WriteDataToMacClipboard(pbData, type);
CFRelease(pbData);
}
}
/*
* ZeroScrap() is called before a Mac application writes to the clipboard; clears out the previous contents
*/
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;
}
/*
* Mac application wrote to clipboard
@ -273,41 +768,69 @@ void GetScrap(void **handle, uint32 type, int32 offset)
void PutScrap(uint32 type, void *scrap, int32 length)
{
static bool clear = true;
D(bug("PutScrap type %4.4s, data %08lx, length %ld\n", &type, scrap, 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;
clear = true;
return;
}
if (length <= 0)
return;
if (clear && PasteboardClear(g_pbref))
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;
if (PasteboardCopyItemFlavorData(g_pbref, (PasteboardItemID)1, STYL_FLAVOR_NAME, &stylData) != noErr)
stylData = NULL;
if (textData != NULL && stylData != NULL) {
CFDataRef rtfData = CreateRTFDataFromMacTEXTAndStyl(textData, stylData);
if (rtfData) {
PasteboardPutItemFlavor(g_pbref, (PasteboardItemID)1, kUTTypeRTF, rtfData, 0);
CFRelease(rtfData);
}
}
if (textData)
CFRelease(textData);
if (stylData)
CFRelease(stylData);
}
}

View File

@ -374,6 +374,14 @@ static void do_putscrap(uint32 type, void *scrap, int32 length)
}
}
/*
* Mac application zeroes clipboard
*/
void ZeroScrap()
{
}
/*
* Mac application reads clipboard

View File

@ -179,6 +179,14 @@ static void do_putscrap(uint32 type, void *scrap, int32 length)
CloseClipboard();
}
/*
* Mac application zeroes clipboard
*/
void ZeroScrap()
{
}
/*
* Mac application reads clipboard

View File

@ -24,6 +24,7 @@
extern void ClipInit(void);
extern void ClipExit(void);
extern void ZeroScrap();
extern void PutScrap(uint32 type, void *scrap, int32 length);
extern void GetScrap(void **handle, uint32 type, int32 offset);

View File

@ -240,6 +240,14 @@ void PutScrap(uint32 type, void *scrap, int32 length)
}
}
/*
* Mac application zeroes clipboard
*/
void ZeroScrap()
{
}
/*
* Mac application reads clipboard

View File

@ -227,6 +227,10 @@ void EmulOp(M68kRegisters *r, uint32 pc, int selector)
Microseconds(r->a[0], r->d[0]);
break;
case OP_ZERO_SCRAP: // ZeroScrap() patch
ZeroScrap();
break;
case OP_PUT_SCRAP: // PutScrap() patch
PutScrap(ReadMacInt32(r->a[7] + 8), Mac2HostAddr(ReadMacInt32(r->a[7] + 4)), ReadMacInt32(r->a[7] + 12));
break;

View File

@ -44,7 +44,7 @@ enum { // Selectors for EMUL_OP opcodes
OP_DISK_OPEN, OP_DISK_PRIME, OP_DISK_CONTROL, OP_DISK_STATUS,
OP_CDROM_OPEN, OP_CDROM_PRIME, OP_CDROM_CONTROL, OP_CDROM_STATUS,
OP_AUDIO_DISPATCH, OP_SOUNDIN_OPEN, OP_SOUNDIN_PRIME, OP_SOUNDIN_CONTROL, OP_SOUNDIN_STATUS, OP_SOUNDIN_CLOSE,
OP_ADBOP, OP_INSTIME, OP_RMVTIME, OP_PRIMETIME, OP_MICROSECONDS, OP_PUT_SCRAP, OP_GET_SCRAP,
OP_ADBOP, OP_INSTIME, OP_RMVTIME, OP_PRIMETIME, OP_MICROSECONDS, OP_ZERO_SCRAP, OP_PUT_SCRAP, OP_GET_SCRAP,
OP_DEBUG_STR, OP_INSTALL_DRIVERS, OP_NAME_REGISTRY, OP_RESET, OP_IRQ,
OP_SCSI_DISPATCH, OP_SCSI_ATOMIC,
OP_CHECK_SYSV, OP_NTRB_17_PATCH, OP_NTRB_17_PATCH2, OP_NTRB_17_PATCH3, OP_NTRB_17_PATCH4, OP_CHECKLOAD,
@ -87,6 +87,7 @@ const uint16 M68K_EMUL_OP_INSTIME = M68K_EMUL_BREAK + OP_INSTIME;
const uint16 M68K_EMUL_OP_RMVTIME = M68K_EMUL_BREAK + OP_RMVTIME;
const uint16 M68K_EMUL_OP_PRIMETIME = M68K_EMUL_BREAK + OP_PRIMETIME;
const uint16 M68K_EMUL_OP_MICROSECONDS = M68K_EMUL_BREAK + OP_MICROSECONDS;
const uint16 M68K_EMUL_OP_ZERO_SCRAP = M68K_EMUL_BREAK + OP_ZERO_SCRAP;
const uint16 M68K_EMUL_OP_PUT_SCRAP = M68K_EMUL_BREAK + OP_PUT_SCRAP;
const uint16 M68K_EMUL_OP_GET_SCRAP = M68K_EMUL_BREAK + OP_GET_SCRAP;
const uint16 M68K_EMUL_OP_DEBUG_STR = M68K_EMUL_BREAK + OP_DEBUG_STR;

View File

@ -60,9 +60,10 @@
// Other ROM addresses
const uint32 CHECK_LOAD_PATCH_SPACE = 0x2fcf00;
const uint32 PUT_SCRAP_PATCH_SPACE = 0x2fcf80;
const uint32 GET_SCRAP_PATCH_SPACE = 0x2fcfc0;
const uint32 ADDR_MAP_PATCH_SPACE = 0x2fd100;
const uint32 ZERO_SCRAP_PATCH_SPACE = 0x2fcf80;
const uint32 PUT_SCRAP_PATCH_SPACE = 0x2fcfc0;
const uint32 GET_SCRAP_PATCH_SPACE = 0x2fd100;
const uint32 ADDR_MAP_PATCH_SPACE = 0x2fd140;
// Global variables
int ROMType; // ROM type
@ -713,6 +714,8 @@ bool PatchROM(void)
// Check that other ROM addresses point to really free regions
if (!check_rom_patch_space(CHECK_LOAD_PATCH_SPACE, 0x40))
return false;
if (!check_rom_patch_space(ZERO_SCRAP_PATCH_SPACE, 0x40))
return false;
if (!check_rom_patch_space(PUT_SCRAP_PATCH_SPACE, 0x40))
return false;
if (!check_rom_patch_space(GET_SCRAP_PATCH_SPACE, 0x40))
@ -2296,6 +2299,16 @@ static bool patch_68k(void)
*wp = htons((level1_int - 12) & 0xffff);
}
// Patch ZeroScrap() for clipboard exchange with host OS
uint32 zero_scrap = find_rom_trap(0xa9fc); // ZeroScrap()
wp = (uint16 *)(ROMBaseHost + ZERO_SCRAP_PATCH_SPACE);
*wp++ = htons(M68K_EMUL_OP_ZERO_SCRAP);
*wp++ = htons(M68K_JMP);
*wp++ = htons((ROMBase + zero_scrap) >> 16);
*wp++ = htons((ROMBase + zero_scrap) & 0xffff);
base = ROMBase + ReadMacInt32(ROMBase + 0x22);
WriteMacInt32(base + 4 * (0xa9fc & 0x3ff), ZERO_SCRAP_PATCH_SPACE);
// Patch PutScrap() for clipboard exchange with host OS
uint32 put_scrap = find_rom_trap(0xa9fe); // PutScrap()
wp = (uint16 *)(ROMBaseHost + PUT_SCRAP_PATCH_SPACE);